├── .github ├── PULL_REQUEST_TEMPLATE.md └── issue_template.md ├── .gitignore ├── .wakatime-project ├── CODEOWNERS ├── CONTRIBUTING.md ├── LICENSE.txt ├── README.md ├── SUPPORT.md ├── _config.yml ├── en ├── LICENSE.txt ├── README.md └── subtitles │ ├── 1. Introduction to iOS 10, Xcode 8 and Swift 3.srt │ ├── 10. Core Data.srt │ ├── 11. Core Data Demo.srt │ ├── 12. Autolayout.srt │ ├── 13. Timer and Animation.srt │ ├── 14. Dynamic Animation Demo.srt │ ├── 15. More Segues.srt │ ├── 16. Alerts and Action Sheets, Notifications, Application Lifecycle, and Persistence.srt │ ├── 17. Accessibility.srt │ ├── 2. MVC; iOS, Xcode and Swift Demonstration.srt │ ├── 3. More Swift and the Foundation Framework.srt │ ├── 4. Views.srt │ ├── 5. Gestures and Multiple MVCs.srt │ ├── 6. Multiple MVCs, View Controller Lifecycle, and Memory Management.srt │ ├── 7. Error Handling, Extensions, Protocols, Delegation, and Scroll View.srt │ ├── 8. Multithreading and Text Field.srt │ └── 9. Table View.srt ├── subtitles ├── 1. Introduction to iOS 10, Xcode 8 and Swift 3.srt ├── 10. Core Data.srt ├── 11. Core Data Demo.srt ├── 12. Autolayout.srt ├── 13. Timer and Animation.srt ├── 14. Dynamic Animation Demo.srt ├── 15. More Segues.srt ├── 16. Alerts and Action Sheets, Notifications, Application Lifecycle, and Persistence.srt ├── 17. Accessibility.srt ├── 2. MVC; iOS, Xcode and Swift Demonstration.srt ├── 3. More Swift and the Foundation Framework.srt ├── 4. Views.srt ├── 5. Gestures and Multiple MVCs.srt ├── 6. Multiple MVCs, View Controller Lifecycle, and Memory Management.srt ├── 7. Error Handling, Extensions, Protocols, Delegation, and Scroll View.srt ├── 8. Multithreading and Text Field.srt └── 9. Table View.srt ├── tools ├── download.md ├── main.swift └── update.xcodeproj │ ├── project.pbxproj │ └── project.xcworkspace │ └── contents.xcworkspacedata └── translation-style-guide.md /.github/PULL_REQUEST_TEMPLATE.md: -------------------------------------------------------------------------------- 1 | ## 类型 2 | - [x] 翻译 3 | - [ ] 校对 4 | - [ ] 其他 5 | 6 | 集数: 7 | 开始段: 8 | 结束段: 9 | 10 | 备注(如果有,请列出): 11 | - 没有则不填 12 | -------------------------------------------------------------------------------- /.github/issue_template.md: -------------------------------------------------------------------------------- 1 | - [ ] 请先查看 [常见问题与解答](../SUPPORT.md) 2 | - [ ] 催更请前往 [Issue #27](https://github.com/ApolloZhu/Developing-iOS-10-Apps-with-Swift/issues/27)。 3 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | bilicookies 2 | .vscode 3 | 4 | # OS generated files # 5 | ###################### 6 | .DS_Store 7 | .DS_Store? 8 | ._* 9 | .Spotlight-V100 10 | .Trashes 11 | ehthumbs.db 12 | Thumbs.db 13 | Desktop.ini 14 | 15 | # Xcode 16 | # 17 | # gitignore contributors: remember to update Global/Xcode.gitignore, Objective-C.gitignore & Swift.gitignore 18 | 19 | ## Build generated 20 | build/ 21 | DerivedData/ 22 | Debug/ 23 | Release/ 24 | 25 | ## Various settings 26 | *.pbxuser 27 | !default.pbxuser 28 | *.mode1v3 29 | !default.mode1v3 30 | *.mode2v3 31 | !default.mode2v3 32 | *.perspectivev3 33 | !default.perspectivev3 34 | xcuserdata/ 35 | 36 | ## Other 37 | *.moved-aside 38 | *.xcuserstate 39 | 40 | ## Obj-C/Swift specific 41 | *.hmap 42 | *.ipa 43 | *.dSYM.zip 44 | *.dSYM 45 | 46 | ## Playgrounds 47 | timeline.xctimeline 48 | playground.xcworkspace 49 | 50 | # Swift Package Manager 51 | # 52 | # Add this line if you want to avoid checking in source code from Swift Package Manager dependencies. 53 | # Packages/ 54 | .build/ 55 | 56 | # CocoaPods 57 | # 58 | # We recommend against adding the Pods directory to your .gitignore. However 59 | # you should judge for yourself, the pros and cons are mentioned at: 60 | # https://guides.cocoapods.org/using/using-cocoapods.html#should-i-check-the-pods-directory-into-source-control 61 | # 62 | # Pods/ 63 | 64 | # Carthage 65 | # 66 | # Add this line if you want to avoid checking in source code from Carthage dependencies. 67 | # Carthage/Checkouts 68 | 69 | Carthage/Build 70 | 71 | # fastlane 72 | # 73 | # It is recommended to not store the screenshots in the git repo. Instead, use fastlane to re-generate the 74 | # screenshots whenever they are needed. 75 | # For more information about the recommended setup visit: 76 | # https://github.com/fastlane/fastlane/blob/master/fastlane/docs/Gitignore.md 77 | 78 | fastlane/report.xml 79 | fastlane/Preview.html 80 | fastlane/screenshots 81 | fastlane/test_output 82 | -------------------------------------------------------------------------------- /.wakatime-project: -------------------------------------------------------------------------------- 1 | Developing-iOS-10-Apps-with-Swift 2 | 3 | -------------------------------------------------------------------------------- /CODEOWNERS: -------------------------------------------------------------------------------- 1 | # Each line is a file pattern followed by one or more owners. 2 | 3 | # These owners will be the default owners for everything in the repo. 4 | * @ApolloZhu @LiulietLee 5 | 6 | # Order is important; the last matching pattern takes the most precedence. 7 | *.srt @ApolloZhu 8 | -------------------------------------------------------------------------------- /CONTRIBUTING.md: -------------------------------------------------------------------------------- 1 | # 任务相关说明 2 | 3 | 欢迎加入翻译的队伍!去年的现在还没有翻译完,为了避免这样的情况再次发生,希望能有更多的小伙伴们加入。Swifters of China, unite! 4 | 5 | 总之大家先 Watch + Star 这个 repository,有兴趣也可以加 QQ 群 277542197。 6 | 7 | ## 领取任务 8 | 9 | 1. 每节新内容发布以后,[issue 区](https://github.com/ApolloZhu/Developing-iOS-10-Apps-with-Swift/issues) 会开一个当节的「任务分配」issue。 10 | 2. 请到每节的 issue 中评论「翻译」或「校对」。评论的内容包括: 11 | 1. 段数。建议选择 issue 下第一条里公布的,一般会是 300 或 500 段,也可以自定义,不过为了方便管理,要求每次至少 100 段。 12 | 2. 预计完成时间。我个人的翻译速度是每小时 50 段,请结合个人情况进行估计,建议控制在一个星期以内。海外党请用本地时,并注明时区。 13 | 3. 我会结合字幕和您的需求等综合考虑后在 issue 中 @你 通知实际翻译的段数。 14 | 4. 请尽快在 issue 下回复 或 发邮件 public-apollonian@outlook.com 或 QQ 群确认。 15 | 5. 之后我会在 [看板](https://github.com/ApolloZhu/Developing-iOS-10-Apps-with-Swift/projects/1) 和 issue 顶部添加关于您任务的信息。 16 | 17 | **翻译、校对任务的段数不是指字幕文件中行数,而是指** 18 | 19 | 67 <----此处的数字 20 | 00:03:04,860 --> 00:03:06,960 21 | we talked about the NSNumber format and all that. 22 | 23 | ## 进行翻译 24 | 25 | 1. 请认真阅读并遵守 [翻译标准/校对规则](./translation-style-guide.md)。 26 | 2. 为了避免其他人也翻译同样的内容,请只翻译分配的段数。如果您不小心翻译了其他部分,请立刻告诉我,避免重复劳动。 27 | 3. 如果忙碌或者有困难一定要及时提出来,我也可以把任务转给其他人。这并不会对您有任何影响,觉得难为情可以邮件/私信我。 28 | 4. Fork 本项目到您的账户下,然后 Clone 保存到本地。 29 | 5. 如果已经 fork 过了,请通过 sync/update from master/fetch origin 等方式完成同步。 30 | 6. 翻译过程中请不要 update from master。 31 | 7. 翻译 **subtitles** 文件夹下对应 srt 文件的对应段,在英文行下面添加中文翻译。 32 | - srt 就是普通的文本文件,所以使用的程序只要能够保证保存为同样格式就行,个人偏好是 Visual Studio Code,也可以直接在 GitHub 上编辑。 33 | - 千万 **不要** 翻译 en/subtitles 文件夹里的,那些是保留原版字幕以备后用。区别方法如下: 34 | - 要翻译的文件所有的英文都只有一行; 35 | - 要翻译的文件至少第一句和最后一句都已经翻译过了。 36 | 8. 建议每完成一部分就 **commit** 一次,这样我们能对进度有个大概的把握。 37 | 9. 翻译或校对的过程中有拿不准的地方,请先尝试按照标准里提到的方法解决,也可以进入该节的 issue 中讨论(如这个词该不该翻译,该翻译成什么等)。 38 | 39 | ## 提交翻译 40 | 41 | 1. **全部** 翻译完之后提交 pull request,你会看到它出现在 [这里](https://github.com/ApolloZhu/Developing-iOS-10-Apps-with-Swift/pulls),具体步骤可以参考 [教程](https://help.github.com/articles/creating-a-pull-request-from-a-fork/)。 42 | 2. 如果有,请在提交信息中注明我们需要特别注意的地方,和其他任务行之外的改动。 43 | 2. 我们会进行简单的校对。多半您在主项目的 pull request 下会被拒绝合并,request changes,这是正常的现象。 44 | 3. 请 merge 您 fork 下的 pull request,建议然后再简单地确认一下。 45 | 4. merge 并修改(如果有)时,主项目的 pull request 会自动更新。请评论表示可以合并到主分支。 46 | 5. 在看到主项目出现一个标题为 `集数_开始段-结束段 翻译 @你` 的 commit 之后就算完成了。如果有需要,这个时候就可以安全地删除 fork 和本地文件了。 47 | 48 | ---- 49 | 50 | 本规则基于 [github.com/SwiftGGTeam/Developing-iOS-9-Apps-with-Swift/issues/3](https://github.com/SwiftGGTeam/Developing-iOS-9-Apps-with-Swift/issues/3) 修订而成。 51 | -------------------------------------------------------------------------------- /LICENSE.txt: -------------------------------------------------------------------------------- 1 | 本项目(包括字幕,代码等),以及原斯坦福课程均采用 知识共享 署名-非商业性使用-相同方式共享 3.0 美国 许可协议 进行许可。协议详见 https://creativecommons.org/licenses/by-nc-sa/3.0/us/deed.zh。 2 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Developing iOS 10 Apps with Swift 字幕简体中文翻译项目 2 | 3 | ## iOS 11 已出,请大家移步 [https://github.com/ApolloZhu/Developing-iOS-11-Apps-with-Swift](https://github.com/ApolloZhu/Developing-iOS-11-Apps-with-Swift) 4 | 5 |
6 | 7 | 已完成前六集翻译,点此下载(字幕在 subtitles 文件夹里),其他课程录像和资料等可以查看下载列表。但本项目在 iOS 11 翻译 完成前不再继续翻译。如有需要可以参考其他项目提供的机器翻译,比如 有道字典机翻另一个机翻+粗校本项目不对机器翻译内容负责。 8 | 9 | 10 | [English Version (Outdated)](./en/README.md) 11 | 12 | [![Backers on Open Collective](https://opencollective.com/Developing-iOS-10-Apps-with-Swift/backers/badge.svg)](#backers) [![Sponsors on Open Collective](https://opencollective.com/Developing-iOS-10-Apps-with-Swift/sponsors/badge.svg)](#sponsors) 13 | 14 | ### 版权说明 15 | 16 | 知识共享许可协议 17 | 18 | 本项目(包括字幕,代码等),以及原斯坦福课程均采用 知识共享 署名-非商业性使用-相同方式共享 3.0 美国 许可协议 进行许可。 19 | 20 | ---- 21 | 22 | 如果您感兴趣,有能力,我们欢迎您参与翻译/校对本项目。详情见 [任务相关说明](./CONTRIBUTING.md)([失效备份](https://github.com/ApolloZhu/Developing-iOS-10-Apps-with-Swift/blob/master/CONTRIBUTING.md))。 23 | 24 | 关于如何获得更新,请查看 [常见问题与解答](./SUPPORT.md) 中的字幕更新章节。 25 | 26 | 如果您想支持我们,请点击项目右上角的 Star 按钮来 Star 本项目。其他如分享和打赏等方式请查看 [常见问题与解答](./SUPPORT.md) 中的支持我们章节。 27 | 28 | ### 下载 29 | 30 | (从 iTunes U 提取),其中点击 `中英字幕` 就可以下载已经翻译好了的字幕。另一个方案是 [下载整个项目](https://github.com/ApolloZhu/Developing-iOS-10-Apps-with-Swift/archive/master.zip)。`subtitles` 文件夹中是中英字幕(不一定都翻译了),`en/subtitles` 文件夹中是纯英文字幕。 31 | 32 | 字幕的格式是 `.srt`,所以您可能需要用带外挂字幕功能的视频播放器,比如 [VLC](http://www.videolan.org/vlc/index.zh.html) 或 [IINA](https://lhc70000.github.io/iina/zh-cn/) 等才可使用。其他字幕使用问题的解答请查看 [常见问题与解答](./SUPPORT.md) 的字幕使用章节。 33 | 34 | 如果您有任何建议或意见,或是在观看视频时发现了翻译有误的地方,请通过 [常见问题与解答](./SUPPORT.md) 中提供的联系方式反馈。 35 | 36 | #### 课程相关资源 37 | 38 | - iTunes U:[Developing iOS 10 Apps with Swift - Free Course by Stanford](https://itunes.apple.com/us/course/developing-ios-10-apps-with-swift/id1198467120) 39 | - [RSS 源](https://p1-u.itunes.apple.com/WebObjects/LZStudent.woa/ra/feed/COETAIHAJLZIQXJI) 40 | - [课程专辑封面](http://a2.mzstatic.com/us/r30/CobaltPublic122/v4/6b/66/d0/6b66d0af-d47f-37d6-9993-9c5237401a49/d3_64_2x.png) 41 | - [首页推广图片](http://a2.mzstatic.com/us/r30/Features122/v4/79/cb/ce/79cbce27-b961-9dfb-f044-21686543edf8/flowcase_1360_520_2x.jpeg) 42 | - CS 193P 课程地址:[CS 193P iPhone Application Development](http://web.stanford.edu/class/cs193p/cgi-bin/drupal/) 43 | - Paul Hegarty:[piazza](https://piazza.com/professors/show/paul_hegarty) 44 | 45 | #### 项目相关资源 46 | 47 | - [Developing iOS 9 Apps with Swift 字幕翻译](https://github.com/SwiftGGTeam/Developing-iOS-9-Apps-with-Swift) 48 | - [Developing iOS 8 Apps with Swift 字幕翻译](https://github.com/X140Yu/Developing_iOS_8_Apps_With_Swift) 49 | - 提取字幕:[CCExtractor](https://www.ccextractor.org/) 50 | - 字幕重排:[X140Yu/Developing_iOS_8_Apps_With_Swift/tools/trim.rb](https://github.com/X140Yu/Developing_iOS_8_Apps_With_Swift/blob/master/tools/trim.rb) 51 | 52 | ---- 53 | 54 | 55 | ## Contributors 56 | 57 | This project exists thanks to all the people who contribute. [[Contribute]](CONTRIBUTING.md). 58 | 59 | 60 | 61 | ## Backers 62 | 63 | Thank you to all our backers! 🙏 [[Become a backer](https://opencollective.com/Developing-iOS-10-Apps-with-Swift#backer)] 64 | 65 | 66 | 67 | 68 | ## Sponsors 69 | 70 | Support this project by becoming a sponsor. Your logo will show up here with a link to your website. [[Become a sponsor](https://opencollective.com/Developing-iOS-10-Apps-with-Swift#sponsor)] 71 | 72 | 73 | 74 | 75 | 76 | 77 | 78 | 79 | 80 | 81 | 82 | 83 | 84 | 91 |
92 | -------------------------------------------------------------------------------- /SUPPORT.md: -------------------------------------------------------------------------------- 1 |
2 | 3 | 已完成前六集翻译,点此下载(字幕在 subtitles 文件夹里),其他课程录像和资料等可以查看下载列表。但本项目在 iOS 11 翻译 完成前不再继续翻译。如有需要可以参考其他项目提供的机器翻译,比如 有道字典机翻另一个机翻+粗校本项目不对机器翻译内容负责。 4 | 5 | 6 | # 常见问题与解答 7 | 8 | 如果您阅读完后还是有问题,请通过 “联系我们” 中提供的方式咨询。 9 | 10 | ## 联系我们 11 | 12 | 推荐 [GitHub Issues](https://github.com/ApolloZhu/Developing-iOS-10-Apps-with-Swift/issues/new),或者发电子邮件到 [public-apollonian@outlook.com](mailto:public-apollonian@outlook.com)。 13 | 14 | ## 字幕使用 15 | 16 | ### 字幕乱码 17 | 18 | 如果字幕有乱码,请点击 [中文乱码报错用 Issue](https://github.com/x140yu/Developing_iOS_8_Apps_With_Swift/issues/131) 进行乱码的报错和寻求解决方案。 19 | 20 | ### 字幕看不清 21 | 22 | 播放器应该都可以设置字幕样式,所以不接受在字幕中加上声明样式的代码。 23 | 24 | 设置方法每个系统每个播放器的每个版本都可能不一样,请自行搜索/研究。 25 | 26 | 推荐样式是白字+黑边,一般情况下都是能看清的。 27 | 28 | ## 字幕更新 29 | 30 | ### 如何关注更新? 31 | 32 | 如果您想收到每集翻译完成的更新,请订阅 [RSS源](https://github.com/ApolloZhu/Developing-iOS-10-Apps-with-Swift/releases.atom)。如果您想要更密切地获得最新动态,您也可以 [关注(Watch)项目的更新](https://github.com/ApolloZhu/Developing-iOS-10-Apps-with-Swift/subscription)。 33 | 34 | ## 支持我们 35 | 36 | 在此先感谢所有关注、分享和参与本项目的所有人! 37 | 38 | ### 您的分享非常重要! 39 | 40 | 除了 ***Star*** 之外,同时也希望您能够通过各种国内外社交平台,如 微信公众号,微博,博客,开发者头条,简书,CSDN 等为我们宣传推广,以**吸引更多的人加入翻译,加快翻译速度**,同时帮助更多有需要的人。 41 | 42 | ### 为什么捐助一直在拖? 43 | 44 | 十分感谢大家的好意,我们之后应该会支持微信、支付宝和其他平台的打赏。资金的用途尚未定,但一定会保持公开透明。可以理解大家的心情,先不论我们不是为了盈利,但是现在翻译还没有完成(而且还经常被催),接受大家的善意会于心有愧。 45 | 46 |
47 | -------------------------------------------------------------------------------- /_config.yml: -------------------------------------------------------------------------------- 1 | theme: jekyll-theme-cayman -------------------------------------------------------------------------------- /en/LICENSE.txt: -------------------------------------------------------------------------------- 1 | This work and the original work by Stanford University are both licensed under the Creative Commons Attribution-Noncommercial-Share Alike 3.0 United States License. The license is available at http://creativecommons.org/licenses/by-nc-sa/3.0/us/. 2 | -------------------------------------------------------------------------------- /en/README.md: -------------------------------------------------------------------------------- 1 | # Simplified Chinese Subtitle Translation Project for Developing iOS 10 Apps with Swift 2 | 3 | [中文版本](../README.md) 4 | 5 | ***This document is outdated. Please refer to the above Chinese version for the latest information.*** 6 | 7 | We have not yet finished translating. You may view our progress by 8 | 9 | - [Watching the repository](https://github.com/ApolloZhu/Developing-iOS-10-Apps-with-Swift/subscription) 10 | - [Viewing Kanban for project progress](https://github.com/ApolloZhu/Developing-iOS-10-Apps-with-Swift/projects/1) 11 | 12 | You may also support us by clicking the Star button on the upper right corner of this repository. Meanwhile, we'd like you to share this repository or website with your friends, through social media platforms, such as reddit, twitter, facebook, tumblr, etc., as well as other developer forums and websites, so we may help more people in need. 13 | 14 | If you are interested in helping us translate, please read [Translation Style Guide/Proofread Guidelines (Chinese)](../translation-style-guide.md) carefully,and refer to [Must knows about collaboration (Chinese)](https://github.com/ApolloZhu/Developing-iOS-10-Apps-with-Swift/issues/2). We reserve rights to close any pull request that doesn't follow these regulations. 15 | 16 | ### Download Subtitles 17 | 18 | Please download the [Project Zip Archive](https://github.com/ApolloZhu/Developing-iOS-10-Apps-with-Swift/archive/master.zip). You can find original subtitles in folder `en/subtitles`, and translated subtitles in folder `subtitles`. 19 | 20 | The subtitles are provided as `.srt` files, therefore, you need video players that will support loading external subtitle files, such as [VLC](http://www.videolan.org/vlc/index.html). If you are unable to decode subtitle files correctly, please search for the solutions form, and/or report new issues to [Encoding Issues (Chinese)](https://github.com/x140yu/Developing_iOS_8_Apps_With_Swift/issues/131). 21 | 22 | ### Download Videos and Other Course Materials 23 | 24 | [Download List](../tools/download.md), which is automatically generated from iTunes U, contains resources available for download from iTunes U. 25 | 26 | ### Other Course Related Resources 27 | 28 | - iTunes U:[Developing iOS 10 Apps with Swift - Free Course by Stanford](https://itunes.apple.com/us/course/developing-ios-10-apps-with-swift/id1198467120) 29 | - [RSS Feed](https://p1-u.itunes.apple.com/WebObjects/LZStudent.woa/ra/feed/COETAIHAJLZIQXJI) 30 | - [Album Cover](http://a2.mzstatic.com/us/r30/CobaltPublic122/v4/6b/66/d0/6b66d0af-d47f-37d6-9993-9c5237401a49/d3_64_2x.png) 31 | - [Showcase Image](http://a2.mzstatic.com/us/r30/Features122/v4/79/cb/ce/79cbce27-b961-9dfb-f044-21686543edf8/flowcase_1360_520_2x.jpeg) 32 | - CS 193P on Stanford website:[CS 193P iPhone Application Development](http://web.stanford.edu/class/cs193p/cgi-bin/drupal/) 33 | 34 | ### Translation Related Resources 35 | 36 | - [Developing iOS 9 Apps with Swift Translation](https://github.com/SwiftGGTeam/Developing-iOS-9-Apps-with-Swift) 37 | - [Developing iOS 8 Apps with Swift Translation](https://github.com/X140Yu/Developing_iOS_8_Apps_With_Swift) 38 | - Tool to extract subtitles:[CCExtractor](https://www.ccextractor.org/) 39 | - Reformatting subtitles:[X140Yu/Developing_iOS_8_Apps_With_Swift/tools/trim.rb](https://github.com/X140Yu/Developing_iOS_8_Apps_With_Swift/blob/master/tools/trim.rb) 40 | 41 | ---- 42 | 43 | ### Copyright Information 44 | 45 | Creative Commons License 46 | 47 | This work and the original work by Stanford University are both licensed under the Creative Commons Attribution-NonCommercial-ShareAlike 3.0 United States License. 48 | 49 | ---- 50 | 51 | 52 | -------------------------------------------------------------------------------- /subtitles/17. Accessibility.srt: -------------------------------------------------------------------------------- 1 | 1 2 | 00:00:00,401 --> 00:00:02,000 3 | 本字幕由志愿者义务贡献,采用许可协议 4 | 知识共享 署名-非商业性使用-相同方式共享 3.0 美国 5 | 6 | 2 7 | 00:00:02,069 --> 00:00:07,506 8 | Stanford University. 9 | 斯坦福大学 10 | 11 | 3 12 | 00:00:07,575 --> 00:00:12,410 13 | >> Okay, welcome to Stanford CS193P, Winter of 2017. 14 | 欢迎参加 2017 年冬季学期斯坦福 15 | 16 | 4 17 | 00:00:12,479 --> 00:00:15,447 18 | This is Developing iOS Applications, 19 | CS193P 课程,iOS 应用程序开发 20 | 21 | 5 22 | 00:00:15,515 --> 00:00:18,416 23 | this is lecture number 17, I said. 24 | 25 | 6 26 | 00:00:18,485 --> 00:00:21,119 27 | Yes, and we have a guest lecturer today 28 | 29 | 7 30 | 00:00:21,188 --> 00:00:24,056 31 | who's gonna talk to us about accessibility. 32 | 33 | 8 34 | 00:00:24,125 --> 00:00:25,223 35 | His name is Skylar Peterson. 36 | 37 | 9 38 | 00:00:25,292 --> 00:00:26,592 39 | Why don't you come up here, Skylar? 40 | 41 | 10 42 | 00:00:28,662 --> 00:00:29,761 43 | It's all yours. 44 | 45 | 11 46 | 00:00:29,830 --> 00:00:31,696 47 | Thank you very much. 48 | 49 | 12 50 | 00:00:31,765 --> 00:00:33,297 51 | All righty. 52 | 53 | 13 54 | 00:00:33,366 --> 00:00:34,499 55 | Good afternoon, everybody. 56 | 57 | 14 58 | 00:00:34,568 --> 00:00:36,468 59 | As he said, my name is Skylar Peterson. 60 | 61 | 15 62 | 00:00:36,536 --> 00:00:39,571 63 | I am a member of Apple's Accessibility team. 64 | 65 | 16 66 | 00:00:39,640 --> 00:00:41,373 67 | If you don't know exactly what I mean by that, 68 | 69 | 17 70 | 00:00:41,442 --> 00:00:42,174 71 | then don't worry. 72 | 73 | 18 74 | 00:00:42,243 --> 00:00:44,609 75 | It's what I'm here to talk to you about today. 76 | 77 | 19 78 | 00:00:44,678 --> 00:00:45,877 79 | I first wanna thank Paul for 80 | 81 | 20 82 | 00:00:45,946 --> 00:00:47,412 83 | inviting us here to speak today. 84 | 85 | 21 86 | 00:00:47,481 --> 00:00:50,215 87 | Hopefully you'll find what I have to say meaningful in some 88 | 89 | 22 90 | 00:00:50,284 --> 00:00:53,085 91 | way, and choose to apply it to your own work in the future. 92 | 93 | 23 94 | 00:00:54,988 --> 00:00:56,054 95 | So before we get started, 96 | 97 | 24 98 | 00:00:56,123 --> 00:00:58,089 99 | let me tell you just a little bit about myself. 100 | 101 | 25 102 | 00:00:58,158 --> 00:01:00,959 103 | I actually graduated from Stanford in 2014. 104 | 105 | 26 106 | 00:01:01,028 --> 00:01:03,195 107 | I studied computer science, and 108 | 109 | 27 110 | 00:01:03,264 --> 00:01:05,230 111 | my concentration was in HCI. 112 | 113 | 28 114 | 00:01:05,299 --> 00:01:07,165 115 | I took this class while I was here, and 116 | 117 | 29 118 | 00:01:07,234 --> 00:01:09,768 119 | it's actually one of the reasons that I fell in love 120 | 121 | 30 122 | 00:01:09,837 --> 00:01:12,737 123 | with iOS as a platform and why I'm in the job I'm in today, 124 | 125 | 31 126 | 00:01:12,806 --> 00:01:14,505 127 | so thank you for that as well. 128 | 129 | 32 130 | 00:01:14,574 --> 00:01:17,209 131 | After I graduated I spent a year and a half working at 132 | 133 | 33 134 | 00:01:17,277 --> 00:01:19,778 135 | an enterprise software company called Citrix, 136 | 137 | 34 138 | 00:01:19,846 --> 00:01:22,514 139 | where I worked on two different iOS applications. 140 | 141 | 35 142 | 00:01:22,582 --> 00:01:24,316 143 | And then a little over a year ago, 144 | 145 | 36 146 | 00:01:24,384 --> 00:01:27,085 147 | I made the jump to the Accessibility team at Apple, 148 | 149 | 37 150 | 00:01:27,154 --> 00:01:28,754 151 | where I focus mainly on iOS. 152 | 153 | 38 154 | 00:01:30,890 --> 00:01:32,123 155 | there's a couple things that I'd like to cover with you. 156 | 157 | 39 158 | 00:01:32,124 --> 00:01:33,357 159 | So for today, 160 | 161 | 40 162 | 00:01:33,427 --> 00:01:34,259 163 | First of all, 164 | 165 | 41 166 | 00:01:34,327 --> 00:01:37,529 167 | what exactly do we mean when we say accessibility? 168 | 169 | 42 170 | 00:01:37,598 --> 00:01:39,197 171 | And once you understand that, 172 | 173 | 43 174 | 00:01:39,265 --> 00:01:42,167 175 | how do you take the tools that we've created for you and 176 | 177 | 44 178 | 00:01:42,236 --> 00:01:45,303 179 | apply them to your own work to make your apps accessible? 180 | 181 | 45 182 | 00:01:45,372 --> 00:01:47,906 183 | So included in that we're gonna take a look at 184 | 185 | 46 186 | 00:01:47,975 --> 00:01:49,474 187 | the UIAccessibility API. 188 | 189 | 47 190 | 00:01:49,542 --> 00:01:52,844 191 | We're gonna take a look at several vision accommodations 192 | 193 | 48 194 | 00:01:52,913 --> 00:01:56,448 195 | that you should consider when designing your applications, 196 | 197 | 49 198 | 00:01:56,517 --> 00:01:59,184 199 | and specifically I'm going to call out dynamic 200 | 201 | 50 202 | 00:01:59,253 --> 00:02:01,019 203 | type as an important feature. 204 | 205 | 51 206 | 00:02:01,088 --> 00:02:04,189 207 | And at the end I'd like to discuss some opportunities 208 | 209 | 52 210 | 00:02:04,258 --> 00:02:07,292 211 | available to you as a developer in the accessibility 212 | 213 | 53 214 | 00:02:07,361 --> 00:02:07,793 215 | space. 216 | 217 | 54 218 | 00:02:07,861 --> 00:02:09,861 219 | So let's get started. 220 | 221 | 55 222 | 00:02:09,930 --> 00:02:14,533 223 | I'd like to begin with a brief video that 224 | 225 | 56 226 | 00:02:14,601 --> 00:02:19,737 227 | Apple produced towards the end of last year. 228 | 229 | 57 230 | 00:02:19,806 --> 00:02:23,609 231 | [MUSIC] 232 | 233 | 58 234 | 00:02:23,677 --> 00:02:26,511 235 | People think that having a disability is a barrier. 236 | 237 | 59 238 | 00:02:26,580 --> 00:02:31,950 239 | [MUSIC]. 240 | 241 | 60 242 | 00:02:32,019 --> 00:02:34,186 243 | But that's not the way I see it. 244 | 245 | 61 246 | 00:02:34,254 --> 00:02:37,021 247 | It. [MUSIC] 248 | 249 | 62 250 | 00:02:37,090 --> 00:02:39,157 251 | You can catch up with friends. 252 | 253 | 63 254 | 00:02:39,226 --> 00:02:41,359 255 | [MUSIC] 256 | 257 | 64 258 | 00:02:41,428 --> 00:02:43,562 259 | Ready? You can capture a moment with 260 | 261 | 65 262 | 00:02:43,631 --> 00:02:44,463 263 | your family. 264 | 265 | 66 266 | 00:02:44,531 --> 00:02:46,865 267 | One face, small face. 268 | 269 | 67 270 | 00:02:46,933 --> 00:02:47,966 271 | Focus log. 272 | 273 | 68 274 | 00:02:48,035 --> 00:02:50,602 275 | [MUSIC] 276 | 277 | 69 278 | 00:02:50,670 --> 00:02:53,304 279 | And you can start the day bright and early. 280 | 281 | 70 282 | 00:02:53,373 --> 00:02:58,076 283 | [MUSIC] 284 | 285 | 71 286 | 00:02:58,145 --> 00:03:00,812 287 | You can take a trip to somewhere new. 288 | 289 | 72 290 | 00:03:00,881 --> 00:03:06,585 291 | [MUSIC] 292 | 293 | 73 294 | 00:03:06,653 --> 00:03:08,286 295 | 3 miles to the summit. 296 | 297 | 74 298 | 00:03:08,355 --> 00:03:09,888 299 | [MUSIC] 300 | 301 | 75 302 | 00:03:09,957 --> 00:03:13,091 303 | You can concentrate on every word of the story. 304 | 305 | 76 306 | 00:03:13,159 --> 00:03:15,560 307 | A bird begun to sing. 308 | 309 | 77 310 | 00:03:15,628 --> 00:03:17,362 311 | Jack opened his eyes. 312 | 313 | 78 314 | 00:03:17,431 --> 00:03:19,664 315 | [MUSIC] 316 | 317 | 79 318 | 00:03:19,733 --> 00:03:22,367 319 | You can take the long way home. 320 | 321 | 80 322 | 00:03:22,436 --> 00:03:32,310 323 | [MUSIC] 324 | 325 | 81 326 | 00:03:32,378 --> 00:03:33,811 327 | Or edit a film. 328 | 329 | 82 330 | 00:03:33,880 --> 00:03:35,346 331 | [MUSIC] 332 | 333 | 83 334 | 00:03:35,415 --> 00:03:36,614 335 | Like this one. 336 | 337 | 84 338 | 00:03:36,683 --> 00:03:43,688 339 | [MUSIC]. 340 | 341 | 85 342 | 00:03:43,757 --> 00:03:46,457 343 | When technology is designed for everyone, 344 | 345 | 86 346 | 00:03:46,526 --> 00:03:48,326 347 | [MUSIC] 348 | 349 | 87 350 | 00:03:48,395 --> 00:03:53,398 351 | It lets anyone do what they love, including me. 352 | 353 | 88 354 | 00:03:53,467 --> 00:03:56,334 355 | [MUSIC] 356 | 357 | 89 358 | 00:03:56,403 --> 00:03:57,769 359 | So the woman narrating and 360 | 361 | 90 362 | 00:03:57,838 --> 00:04:00,705 363 | whom you saw featured throughout that video is named 364 | 365 | 91 366 | 00:04:00,773 --> 00:04:03,375 367 | Sady, and she has cerebral palsy. 368 | 369 | 92 370 | 00:04:03,444 --> 00:04:06,578 371 | And she's the principal editor on that video, 372 | 373 | 93 374 | 00:04:06,646 --> 00:04:09,481 375 | which I think is a powerful example of the way in which 376 | 377 | 94 378 | 00:04:09,549 --> 00:04:12,483 379 | technology can empower people with disabilities and 380 | 381 | 95 382 | 00:04:12,552 --> 00:04:14,586 383 | fundamentally change their lives. 384 | 385 | 96 386 | 00:04:14,654 --> 00:04:16,955 387 | So that is really what accessibility is all about. 388 | 389 | 97 390 | 00:04:17,024 --> 00:04:20,325 391 | It's making technology work for everyone, 392 | 393 | 98 394 | 00:04:20,393 --> 00:04:23,061 395 | making it accessible for everyone. 396 | 397 | 99 398 | 00:04:23,130 --> 00:04:26,131 399 | So let's take a look at some statistics. 400 | 401 | 100 402 | 00:04:26,199 --> 00:04:30,035 403 | There are 285 million people worldwide who have some sort 404 | 405 | 101 406 | 00:04:30,103 --> 00:04:33,371 407 | of low vision condition or who are entirely blind. 408 | 409 | 102 410 | 00:04:33,440 --> 00:04:38,076 411 | There are 360 million who are hard of hearing or deaf. 412 | 413 | 103 414 | 00:04:38,145 --> 00:04:42,947 415 | One in every 12 men is colorblind in some way, and 1 416 | 417 | 104 418 | 00:04:43,016 --> 00:04:48,119 419 | in 68 children fall somewhere on the autism spectrum. 420 | 421 | 105 422 | 00:04:48,187 --> 00:04:51,322 423 | And so what this means is that worldwide there are over 424 | 425 | 106 426 | 00:04:51,391 --> 00:04:54,525 427 | a billion people that have a disability of some sort. 428 | 429 | 107 430 | 00:04:54,594 --> 00:04:56,795 431 | Or in other words, one in seven. 432 | 433 | 108 434 | 00:04:56,864 --> 00:04:59,864 435 | So by no means is accessibility a small issue. 436 | 437 | 109 438 | 00:04:59,933 --> 00:05:01,733 439 | It affects a lot of people and 440 | 441 | 110 442 | 00:05:01,802 --> 00:05:05,570 443 | if your app has an audience of any significant size you're 444 | 445 | 111 446 | 00:05:05,638 --> 00:05:08,573 447 | going to have users that have disabilities. 448 | 449 | 112 450 | 00:05:08,641 --> 00:05:11,743 451 | And you should be doing the best, your best to make sure 452 | 453 | 113 454 | 00:05:11,812 --> 00:05:14,946 455 | that whatever you're working on works for everybody. 456 | 457 | 114 458 | 00:05:15,015 --> 00:05:18,449 459 | So at Apple we think of accessibility in four distinct 460 | 461 | 115 462 | 00:05:18,518 --> 00:05:21,519 463 | groups. 464 | 465 | 116 466 | 00:05:21,588 --> 00:05:24,789 467 | which includes things like autism, dyslexia, and 468 | 469 | 117 470 | 00:05:24,857 --> 00:05:27,225 471 | other forms of learning disability. 472 | 473 | 118 474 | 00:05:27,294 --> 00:05:30,395 475 | Physical and motor skills, so people that have tremors or 476 | 477 | 119 478 | 00:05:30,463 --> 00:05:33,398 479 | Parkinson's and maybe can't make precise movements 480 | 481 | 120 482 | 00:05:33,467 --> 00:05:36,001 483 | or gestures, but this also includes people who 484 | 485 | 121 486 | 00:05:36,069 --> 00:05:38,670 487 | are paralyzed or have a limited set of digits that 488 | 489 | 122 490 | 00:05:38,739 --> 00:05:40,938 491 | they can use to interact with the system. 492 | 493 | 123 494 | 00:05:41,007 --> 00:05:43,641 495 | For instance a single finger or a single toe, or 496 | 497 | 124 498 | 00:05:43,710 --> 00:05:45,477 499 | in Sadie's case, her head. 500 | 501 | 125 502 | 00:05:47,381 --> 00:05:51,049 503 | Vision, which covers a spectrum of vision impairment 504 | 505 | 126 506 | 00:05:51,118 --> 00:05:53,885 507 | from low vision to completely blind, and 508 | 509 | 127 510 | 00:05:53,953 --> 00:05:57,455 511 | also includes different forms of color blindness. 512 | 513 | 128 514 | 00:05:57,524 --> 00:05:59,056 515 | And finally we have hearing, 516 | 517 | 129 518 | 00:05:59,125 --> 00:06:02,160 519 | which also like vision exists on a spectrum from the hard of 520 | 521 | 130 522 | 00:06:02,229 --> 00:06:04,096 523 | hearing to the completely deaf. 524 | 525 | 131 526 | 00:06:05,865 --> 00:06:09,200 527 | So now our team has developed a huge set of features on iOS 528 | 529 | 132 530 | 00:06:09,268 --> 00:06:12,603 531 | that assists in all of these categories, and best of all 532 | 533 | 133 534 | 00:06:12,672 --> 00:06:16,040 535 | they all come baked into the system from the beginning. 536 | 537 | 134 538 | 00:06:16,109 --> 00:06:19,410 539 | So they're all on all of your iOS devices that you can turn 540 | 541 | 135 542 | 00:06:19,479 --> 00:06:21,112 543 | on, no add-ons required. 544 | 545 | 136 546 | 00:06:21,181 --> 00:06:23,681 547 | And as much as I'd like to go feature by feature and 548 | 549 | 137 550 | 00:06:23,750 --> 00:06:26,684 551 | tell you why each of these is cool, and why it's necessary 552 | 553 | 138 554 | 00:06:26,753 --> 00:06:29,454 555 | and solves the problem, we don't have all night, so 556 | 557 | 139 558 | 00:06:29,523 --> 00:06:31,356 559 | I'm going to focus on just a couple. 560 | 561 | 140 562 | 00:06:31,425 --> 00:06:34,759 563 | But I do encourage you to go and check them out for 564 | 565 | 141 566 | 00:06:34,828 --> 00:06:36,561 567 | You can find accessibility settings on your device by 568 | 569 | 142 570 | 00:06:36,562 --> 00:06:38,295 571 | yourself. 572 | 573 | 143 574 | 00:06:38,365 --> 00:06:39,631 575 | going to General and 576 | 577 | 144 578 | 00:06:39,699 --> 00:06:43,501 579 | tapping on Accessibility where you'll find a long list of 580 | 581 | 145 582 | 00:06:43,570 --> 00:06:47,071 583 | all the features that you can enable on your devices. 584 | 585 | 146 586 | 00:06:47,140 --> 00:06:49,340 587 | What I'd like to do now though is demo for you, 588 | 589 | 147 590 | 00:06:49,409 --> 00:06:52,343 591 | one of our most popular pieces of assistive technology which 592 | 593 | 148 594 | 00:06:52,412 --> 00:06:53,444 595 | is called VoiceOver. 596 | 597 | 149 598 | 00:06:53,513 --> 00:06:56,681 599 | It's a screen reader that allows blind and low vision 600 | 601 | 150 602 | 00:06:56,750 --> 00:07:00,351 603 | users to use a touch screen by audibly describing elements of 604 | 605 | 151 606 | 00:07:00,420 --> 00:07:02,887 607 | the screen as they pan or swipe around it. 608 | 609 | 152 610 | 00:07:02,956 --> 00:07:05,556 611 | So without a technology like this, blind users would be 612 | 613 | 153 614 | 00:07:05,625 --> 00:07:08,426 615 | unable to use the iPhone which is why it's really important 616 | 617 | 154 618 | 00:07:08,494 --> 00:07:11,062 619 | for you to take advantage of the API which I'm gonna get 620 | 621 | 155 622 | 00:07:11,130 --> 00:07:13,865 623 | into in a little bit so that your apps play well with it. 624 | 625 | 156 626 | 00:07:17,804 --> 00:07:20,371 627 | So, I have my device here on the home screen and 628 | 629 | 157 630 | 00:07:20,440 --> 00:07:22,106 631 | I'm gonna turn VoiceOver on. 632 | 633 | 158 634 | 00:07:22,175 --> 00:07:25,543 635 | And what you're gonna notice is that it's going to focus on 636 | 637 | 159 638 | 00:07:25,612 --> 00:07:27,445 639 | the first element of the screen, 640 | 641 | 160 642 | 00:07:27,514 --> 00:07:30,114 643 | which is the Mail app in the upper left corner. 644 | 645 | 161 646 | 00:07:30,183 --> 00:07:31,349 647 | VoiceOver on. 648 | 649 | 162 650 | 00:07:31,418 --> 00:07:32,083 651 | Mail. 652 | 653 | 163 654 | 00:07:32,152 --> 00:07:33,751 655 | No unread emails. 656 | 657 | 164 658 | 00:07:33,820 --> 00:07:36,520 659 | And it described it to me, and told me some extra information 660 | 661 | 165 662 | 00:07:36,589 --> 00:07:39,123 663 | about it, for instance, that I have no unread emails. 664 | 665 | 166 666 | 00:07:39,192 --> 00:07:43,160 667 | So if I wanted to navigate between items, 668 | 669 | 167 670 | 00:07:43,229 --> 00:07:47,731 671 | if I wanna go item by item, I can swipe right or left. 672 | 673 | 168 674 | 00:07:47,800 --> 00:07:50,568 675 | Calendar, photos, camera. 676 | 677 | 169 678 | 00:07:50,637 --> 00:07:53,805 679 | And it's going to read to me the description of each 680 | 681 | 170 682 | 00:07:53,874 --> 00:07:55,140 683 | But if we're on the home screen which I'm here fairly 684 | 685 | 171 686 | 00:07:55,141 --> 00:07:56,407 687 | item as I go. 688 | 689 | 172 690 | 00:07:56,476 --> 00:07:59,077 691 | frequently, I know my way around, I can also just feel 692 | 693 | 173 694 | 00:07:59,146 --> 00:08:01,145 695 | my way around the screen with my finger. 696 | 697 | 174 698 | 00:08:01,214 --> 00:08:04,415 699 | You tapped on Music. 700 | 701 | 175 702 | 00:08:04,484 --> 00:08:06,952 703 | And once I found exactly what I'm looking for, 704 | 705 | 176 706 | 00:08:07,020 --> 00:08:09,487 707 | a double tap acts like a single tap would when you 708 | 709 | 177 710 | 00:08:09,556 --> 00:08:10,955 711 | don't have VoiceOver on. 712 | 713 | 178 714 | 00:08:11,024 --> 00:08:12,023 715 | So it's gonna open Music. 716 | 717 | 179 718 | 00:08:15,795 --> 00:08:18,129 719 | Music, this is now playing screen, button. 720 | 721 | 180 722 | 00:08:18,198 --> 00:08:21,899 723 | Now because music is a first party application, 724 | 725 | 181 726 | 00:08:21,968 --> 00:08:23,134 727 | Apple engineers have gone through and 728 | 729 | 182 730 | 00:08:23,203 --> 00:08:26,237 731 | made sure that all of the UI in that app is accessible. 732 | 733 | 183 734 | 00:08:26,306 --> 00:08:29,106 735 | So VoiceOver interacts with this interface 736 | 737 | 184 738 | 00:08:29,175 --> 00:08:31,042 739 | exactly the same way that it would on the home screen, 740 | 741 | 185 742 | 00:08:31,111 --> 00:08:33,978 743 | I could swipe, [SOUND] Or 744 | 745 | 186 746 | 00:08:34,046 --> 00:08:35,580 747 | find exactly what I'm looking for. 748 | 749 | 187 750 | 00:08:37,083 --> 00:08:41,452 751 | Now VoiceOver, because it is intercepting gestures 752 | 753 | 188 754 | 00:08:41,521 --> 00:08:42,653 755 | that are normally used for 756 | 757 | 189 758 | 00:08:42,722 --> 00:08:44,956 759 | other things when VoiceOver's turned off. 760 | 761 | 190 762 | 00:08:45,024 --> 00:08:46,925 763 | There are analogs for 764 | 765 | 191 766 | 00:08:46,993 --> 00:08:49,160 767 | each different kind of interaction with the system 768 | 769 | 192 770 | 00:08:49,229 --> 00:08:51,362 771 | that you might need when VoiceOver is turned on. 772 | 773 | 193 774 | 00:08:51,430 --> 00:08:55,566 775 | So, for instance, with the track scrubber, if I only want 776 | 777 | 194 778 | 00:08:55,635 --> 00:08:57,936 779 | to scrub through the track, if I don't have voice over on, 780 | 781 | 195 782 | 00:08:58,004 --> 00:09:00,137 783 | then I'm just gonna run my finger along it, 784 | 785 | 196 786 | 00:09:00,206 --> 00:09:01,205 787 | like a slider. 788 | 789 | 197 790 | 00:09:01,274 --> 00:09:04,742 791 | But when VoiceOver's on it's trying to 792 | 793 | 198 794 | 00:09:04,811 --> 00:09:07,312 795 | interpret that gesture as me trying to find an element. 796 | 797 | 199 798 | 00:09:07,381 --> 00:09:10,948 799 | So instead we have this adjustable gesture on 800 | 801 | 200 802 | 00:09:11,017 --> 00:09:13,484 803 | items that are adjustable, I can swipe my finger up and 804 | 805 | 201 806 | 00:09:13,553 --> 00:09:20,258 807 | down to change the value. 808 | 809 | 202 810 | 00:09:20,327 --> 00:09:21,926 811 | 8 seconds and 3 minutes. 812 | 813 | 203 814 | 00:09:21,995 --> 00:09:22,961 815 | 30 seconds. 816 | 817 | 204 818 | 00:09:23,030 --> 00:09:24,729 819 | So those are just some very, 820 | 821 | 205 822 | 00:09:24,798 --> 00:09:27,565 823 | very basic examples of how VoiceOver works. 824 | 825 | 206 826 | 00:09:27,634 --> 00:09:29,934 827 | It's much more advanced than that when you get into 828 | 829 | 207 830 | 00:09:30,003 --> 00:09:31,469 831 | the nitty, nitty gritty of it. 832 | 833 | 208 834 | 00:09:31,537 --> 00:09:33,604 835 | And I highly encourage you to turn it on and 836 | 837 | 209 838 | 00:09:33,673 --> 00:09:36,507 839 | play around with it and see what the experience is really 840 | 841 | 210 842 | 00:09:36,576 --> 00:09:38,042 843 | like for a user who's blind. 844 | 845 | 211 846 | 00:09:38,111 --> 00:09:41,245 847 | So where does the information that an assistive technology 848 | 849 | 212 850 | 00:09:41,314 --> 00:09:43,548 851 | like VoiceOver uses coming from? 852 | 853 | 213 854 | 00:09:43,616 --> 00:09:46,785 855 | Well it runs in it's own process on the system, and 856 | 857 | 214 858 | 00:09:46,853 --> 00:09:49,854 859 | it uses the UIAccessibility protocol to communicate with 860 | 861 | 215 862 | 00:09:49,923 --> 00:09:51,288 863 | other processes, and 864 | 865 | 216 866 | 00:09:51,357 --> 00:09:55,960 867 | ask them information about the current element on the screen. 868 | 869 | 217 870 | 00:09:56,029 --> 00:09:57,761 871 | So VoiceOver will receive an input, for 872 | 873 | 218 874 | 00:09:57,830 --> 00:10:00,932 875 | instance a single tap, and 876 | 877 | 219 878 | 00:10:01,000 --> 00:10:03,902 879 | it will get the currently focused element's description. 880 | 881 | 220 882 | 00:10:03,970 --> 00:10:06,805 883 | It will query the current process for predefined pieces 884 | 885 | 221 886 | 00:10:06,873 --> 00:10:09,106 887 | of information about that element. 888 | 889 | 222 890 | 00:10:09,175 --> 00:10:11,842 891 | And from those queries, it's going to stitch together 892 | 893 | 223 894 | 00:10:11,911 --> 00:10:14,079 895 | a user friendly description and speak it. 896 | 897 | 224 898 | 00:10:16,316 --> 00:10:18,649 899 | So let's take a look at the different properties in 900 | 901 | 225 902 | 00:10:18,718 --> 00:10:21,152 903 | the protocol that an assistive technology is going to 904 | 905 | 226 906 | 00:10:21,220 --> 00:10:22,620 907 | ask about. 908 | 909 | 227 910 | 00:10:22,689 --> 00:10:24,689 911 | The first, and arguably most important, 912 | 913 | 228 914 | 00:10:24,757 --> 00:10:27,658 915 | is isAccessibilityElement which tells VoiceOver whether 916 | 917 | 229 918 | 00:10:27,727 --> 00:10:30,928 919 | or not the current thing should even be visible to it. 920 | 921 | 230 922 | 00:10:30,997 --> 00:10:33,664 923 | And if this is set to no then obviously it's not going to be 924 | 925 | 231 926 | 00:10:33,733 --> 00:10:34,699 927 | an accessible element and 928 | 929 | 232 930 | 00:10:34,768 --> 00:10:37,068 931 | VoiceOver will not be able to focus on it. 932 | 933 | 233 934 | 00:10:37,137 --> 00:10:40,572 935 | But in the case of say, a volume slider, 936 | 937 | 234 938 | 00:10:40,641 --> 00:10:42,006 939 | which I'm going to use as an example for 940 | 941 | 235 942 | 00:10:42,075 --> 00:10:44,376 943 | all these properties, we're gonna set this value to true. 944 | 945 | 236 946 | 00:10:46,412 --> 00:10:48,312 947 | Next we have the accessibilityLabel 948 | 949 | 237 950 | 00:10:48,381 --> 00:10:50,915 951 | which describes what an element. 952 | 953 | 238 954 | 00:10:50,984 --> 00:10:55,153 955 | So in the case of the Volume slider, it changes the volume, 956 | 957 | 239 958 | 00:10:55,222 --> 00:10:56,454 959 | so it's going to say Volume. 960 | 961 | 240 962 | 00:10:56,523 --> 00:10:59,524 963 | And then you may wonder 964 | 965 | 241 966 | 00:10:59,593 --> 00:11:02,426 967 | why we don't say something like Volume Slider. 968 | 969 | 242 970 | 00:11:02,495 --> 00:11:03,661 971 | And this is because of this next 972 | 973 | 243 974 | 00:11:03,730 --> 00:11:05,696 975 | property accessibilityTraits 976 | 977 | 244 978 | 00:11:05,765 --> 00:11:09,801 979 | which is used to define what category an element fits into. 980 | 981 | 245 982 | 00:11:09,869 --> 00:11:12,170 983 | So in the case of a slider, it's an adjustable element, 984 | 985 | 246 986 | 00:11:12,239 --> 00:11:14,339 987 | so we're going to give it the adjustable trait. 988 | 989 | 247 990 | 00:11:15,842 --> 00:11:17,542 991 | This means that when VoiceOver describes it, 992 | 993 | 248 994 | 00:11:17,611 --> 00:11:20,511 995 | it's going to say volume adjustable, 996 | 997 | 249 998 | 00:11:20,580 --> 00:11:23,280 999 | which means that the word slider is redundant. 1000 | 1001 | 250 1002 | 00:11:23,349 --> 00:11:24,315 1003 | And doesn't need to be there. 1004 | 1005 | 251 1006 | 00:11:25,752 --> 00:11:26,817 1007 | Accessibility traits 1008 | 1009 | 252 1010 | 00:11:26,886 --> 00:11:29,253 1011 | are important in other ways as well. 1012 | 1013 | 253 1014 | 00:11:29,322 --> 00:11:33,290 1015 | So certain traits afford elements certain behaviors. 1016 | 1017 | 254 1018 | 00:11:33,359 --> 00:11:36,060 1019 | For instance, in my demo where I was showing the track 1020 | 1021 | 255 1022 | 00:11:36,129 --> 00:11:40,331 1023 | scrubber, that increment and decrement gesture is added 1024 | 1025 | 256 1026 | 00:11:40,400 --> 00:11:44,134 1027 | because that view has the adjustable trait set to it. 1028 | 1029 | 257 1030 | 00:11:44,203 --> 00:11:46,771 1031 | So, VoiceOver knows that that view is going to respond 1032 | 1033 | 258 1034 | 00:11:46,839 --> 00:11:50,942 1035 | in some way to having its value increased or decreased. 1036 | 1037 | 259 1038 | 00:11:52,345 --> 00:11:54,545 1039 | VoiceOver also has a mechanism for 1040 | 1041 | 260 1042 | 00:11:54,614 --> 00:11:57,982 1043 | navigating a display, where a user can say 1044 | 1045 | 261 1046 | 00:11:58,050 --> 00:12:01,119 1047 | I want to navigate based on only this kind of item. 1048 | 1049 | 262 1050 | 00:12:01,187 --> 00:12:04,422 1051 | So, for instance, if I had a table view that I wanted to 1052 | 1053 | 263 1054 | 00:12:04,491 --> 00:12:08,593 1055 | navigate just by its headers then I could set this in 1056 | 1057 | 264 1058 | 00:12:08,661 --> 00:12:11,562 1059 | VoiceOver, and it's going to go through and only focus on 1060 | 1061 | 265 1062 | 00:12:11,631 --> 00:12:14,698 1063 | elements in the hierarchy that have the header trait. 1064 | 1065 | 266 1066 | 00:12:14,767 --> 00:12:17,001 1067 | So it's important that all of your views have the right 1068 | 1069 | 267 1070 | 00:12:17,070 --> 00:12:19,971 1071 | traits so they behave as expected to voice over users. 1072 | 1073 | 268 1074 | 00:12:22,375 --> 00:12:24,442 1075 | Next we have accessibility value. 1076 | 1077 | 269 1078 | 00:12:24,511 --> 00:12:27,745 1079 | Which you can override if your view has some sort of state. 1080 | 1081 | 270 1082 | 00:12:27,814 --> 00:12:29,747 1083 | So for example, the state for 1084 | 1085 | 271 1086 | 00:12:29,816 --> 00:12:32,283 1087 | the volume slider is what is the current percent 1088 | 1089 | 272 1090 | 00:12:32,352 --> 00:12:35,219 1091 | that my volume is set to, and we're gonna return 50%. 1092 | 1093 | 273 1094 | 00:12:35,288 --> 00:12:39,757 1095 | And finally, we have accessibilityHint. 1096 | 1097 | 274 1098 | 00:12:39,825 --> 00:12:42,593 1099 | So this is another optional property that's meant 1100 | 1101 | 275 1102 | 00:12:42,662 --> 00:12:45,129 1103 | to provide a more long-form description of what something 1104 | 1105 | 276 1106 | 00:12:45,198 --> 00:12:47,398 1107 | is or what something does. 1108 | 1109 | 277 1110 | 00:12:47,467 --> 00:12:51,035 1111 | This is important often for VoiceOver users who have never 1112 | 1113 | 278 1114 | 00:12:51,104 --> 00:12:53,705 1115 | used your interface before and are trying to learn their way 1116 | 1117 | 279 1118 | 00:12:53,773 --> 00:12:57,575 1119 | around it and figure out how things work together. 1120 | 1121 | 280 1122 | 00:12:57,643 --> 00:13:00,211 1123 | But you should never put information that is 1124 | 1125 | 281 1126 | 00:13:00,280 --> 00:13:03,381 1127 | critical to the user in this description 1128 | 1129 | 282 1130 | 00:13:03,450 --> 00:13:06,384 1131 | because Accessibility can be turned off. 1132 | 1133 | 283 1134 | 00:13:06,452 --> 00:13:08,619 1135 | And therefore a user may not ever hear them. 1136 | 1137 | 284 1138 | 00:13:08,688 --> 00:13:11,756 1139 | Which they might do to keep VoiceOver from being 1140 | 1141 | 285 1142 | 00:13:11,825 --> 00:13:14,825 1143 | too verbose if they're an experienced user. 1144 | 1145 | 286 1146 | 00:13:14,894 --> 00:13:18,662 1147 | So those are the fine five main accessibility traits. 1148 | 1149 | 287 1150 | 00:13:18,731 --> 00:13:21,599 1151 | Without them the interface is totally unusable to VoiceOver, 1152 | 1153 | 288 1154 | 00:13:21,667 --> 00:13:25,036 1155 | and that's the blind and low vision users who rely on it. 1156 | 1157 | 289 1158 | 00:13:25,104 --> 00:13:29,674 1159 | UIKit views define these properties by default, or 1160 | 1161 | 290 1162 | 00:13:29,742 --> 00:13:32,543 1163 | most of them do, which means that most of the legwork is 1164 | 1165 | 291 1166 | 00:13:32,611 --> 00:13:34,078 1167 | already done for you. 1168 | 1169 | 292 1170 | 00:13:34,147 --> 00:13:35,612 1171 | But what you really need to watch out for 1172 | 1173 | 293 1174 | 00:13:35,681 --> 00:13:39,551 1175 | is your own custom views, so let's look at some code. 1176 | 1177 | 294 1178 | 00:13:41,154 --> 00:13:43,788 1179 | Let's say that I have a view controller 1180 | 1181 | 295 1182 | 00:13:43,856 --> 00:13:47,925 1183 | that has a property image view of subclass MyImageView, 1184 | 1185 | 296 1186 | 00:13:47,994 --> 00:13:50,695 1187 | which is a subclass that I've created. 1188 | 1189 | 297 1190 | 00:13:50,764 --> 00:13:52,764 1191 | It's just a UIView, it's not a UIImageView or 1192 | 1193 | 298 1194 | 00:13:52,832 --> 00:13:54,131 1195 | anything like that, so 1196 | 1197 | 299 1198 | 00:13:54,200 --> 00:13:55,767 1199 | it doesn't have accessibility by default. 1200 | 1201 | 300 1202 | 00:13:57,437 --> 00:13:59,436 1203 | Well in my viewDidLoad method I'm obviously gonna call 1204 | 1205 | 301 1206 | 00:13:59,505 --> 00:14:01,572 1207 | super and then I could do this. 1208 | 1209 | 302 1210 | 00:14:01,640 --> 00:14:04,342 1211 | I could use dot notation to set each of these different 1212 | 1213 | 303 1214 | 00:14:04,411 --> 00:14:05,275 1215 | properties. 1216 | 1217 | 304 1218 | 00:14:05,344 --> 00:14:06,277 1219 | I'm gonna say yes my 1220 | 1221 | 305 1222 | 00:14:06,345 --> 00:14:08,179 1223 | imageView.isAccessibilityElem- ent. 1224 | 1225 | 306 1226 | 00:14:08,248 --> 00:14:12,550 1227 | I'm gonna give it some reasonable label and I'm gonna 1228 | 1229 | 307 1230 | 00:14:12,618 --> 00:14:15,185 1231 | give it the trait, the image trait, because I want it to 1232 | 1233 | 308 1234 | 00:14:15,254 --> 00:14:17,322 1235 | behave like any other image would on the system. 1236 | 1237 | 309 1238 | 00:14:18,658 --> 00:14:21,258 1239 | Now, I probably want to do this in a case where these 1240 | 1241 | 310 1242 | 00:14:21,327 --> 00:14:25,963 1243 | values depend on an instance of a view. 1244 | 1245 | 311 1246 | 00:14:26,032 --> 00:14:28,565 1247 | But, there may be a case where 1248 | 1249 | 312 1250 | 00:14:28,634 --> 00:14:31,602 1251 | the value that is returned from one of these properties 1252 | 1253 | 313 1254 | 00:14:31,671 --> 00:14:33,270 1255 | applies to all view of that kind. 1256 | 1257 | 314 1258 | 00:14:33,339 --> 00:14:37,475 1259 | So, on the subclass MyImageView, 1260 | 1261 | 315 1262 | 00:14:37,544 --> 00:14:41,879 1263 | I could have instead overridden the getter for 1264 | 1265 | 316 1266 | 00:14:41,948 --> 00:14:45,182 1267 | isAccessibilityElement always return true. 1268 | 1269 | 317 1270 | 00:14:45,251 --> 00:14:47,451 1271 | If I know that MyImageView is never not 1272 | 1273 | 318 1274 | 00:14:47,520 --> 00:14:48,585 1275 | going to be an accessibility 1276 | 1277 | 319 1278 | 00:14:48,654 --> 00:14:50,588 1279 | element than this is probably what I want to do. 1280 | 1281 | 320 1282 | 00:14:50,657 --> 00:14:53,791 1283 | Or if there's some internal logic to that class that 1284 | 1285 | 321 1286 | 00:14:53,860 --> 00:14:55,793 1287 | determines whether or not I should determine true or 1288 | 1289 | 322 1290 | 00:14:55,862 --> 00:14:58,762 1291 | false from here, this is a much better way to do that. 1292 | 1293 | 323 1294 | 00:14:58,831 --> 00:15:00,899 1295 | And then I can override the setter to just do nothing 1296 | 1297 | 324 1298 | 00:15:02,435 --> 00:15:04,802 1299 | this applies to all of the properties. 1300 | 1301 | 325 1302 | 00:15:04,871 --> 00:15:06,570 1303 | I can override them, their getters and 1304 | 1305 | 326 1306 | 00:15:06,639 --> 00:15:07,639 1307 | setters explicitly. 1308 | 1309 | 327 1310 | 00:15:10,510 --> 00:15:13,444 1311 | So, now I'd like to demo what you've just seen 1312 | 1313 | 328 1314 | 00:15:13,513 --> 00:15:14,945 1315 | on a sample app that I've been working on. 1316 | 1317 | 329 1318 | 00:15:15,014 --> 00:15:19,349 1319 | So I'm gonna go back over here to my device and 1320 | 1321 | 330 1322 | 00:15:19,418 --> 00:15:20,618 1323 | I'm gonna turn VoiceOver back on. 1324 | 1325 | 331 1326 | 00:15:21,788 --> 00:15:22,353 1327 | VoiceOver on. 1328 | 1329 | 332 1330 | 00:15:23,789 --> 00:15:26,257 1331 | So you see down here I have this app called Notecards. 1332 | 1333 | 333 1334 | 00:15:26,326 --> 00:15:28,626 1335 | [INAUDIBLE] Note note cards. 1336 | 1337 | 334 1338 | 00:15:28,694 --> 00:15:33,264 1339 | It's just a basic app that has stacks of notecards that I 1340 | 1341 | 335 1342 | 00:15:33,333 --> 00:15:35,232 1343 | can use to test myself. 1344 | 1345 | 336 1346 | 00:15:35,301 --> 00:15:37,535 1347 | And I need to do an accessibility audit of this 1348 | 1349 | 337 1350 | 00:15:37,603 --> 00:15:40,104 1351 | app to see how well it works with VoiceOver. 1352 | 1353 | 338 1354 | 00:15:40,172 --> 00:15:42,139 1355 | So, I'm gonna start by tapping on the first thing I see, 1356 | 1357 | 339 1358 | 00:15:42,208 --> 00:15:43,207 1359 | which is that collection header. 1360 | 1361 | 340 1362 | 00:15:43,276 --> 00:15:46,010 1363 | Collection. 1364 | 1365 | 341 1366 | 00:15:46,079 --> 00:15:50,814 1367 | So, as you saw it read the label collection but 1368 | 1369 | 342 1370 | 00:15:50,883 --> 00:15:52,583 1371 | what it didn't say was header. 1372 | 1373 | 343 1374 | 00:15:52,651 --> 00:15:54,952 1375 | So I have no idea that this thing 1376 | 1377 | 344 1378 | 00:15:55,021 --> 00:15:57,488 1379 | is actually a label that is meant to be a header for 1380 | 1381 | 345 1382 | 00:15:57,557 --> 00:15:58,789 1383 | all of the content underneath it. 1384 | 1385 | 346 1386 | 00:15:58,858 --> 00:16:01,125 1387 | So that's the first thing I need to do is add that trait. 1388 | 1389 | 347 1390 | 00:16:02,629 --> 00:16:03,294 1391 | Add button. 1392 | 1393 | 348 1394 | 00:16:03,362 --> 00:16:05,329 1395 | That button has a sensible label. 1396 | 1397 | 349 1398 | 00:16:05,398 --> 00:16:07,198 1399 | Physics five cards. 1400 | 1401 | 350 1402 | 00:16:07,267 --> 00:16:07,865 1403 | That seems right. 1404 | 1405 | 351 1406 | 00:16:07,934 --> 00:16:10,834 1407 | Image one button. 1408 | 1409 | 352 1410 | 00:16:10,903 --> 00:16:11,702 1411 | That doesn't seem right. 1412 | 1413 | 353 1414 | 00:16:11,771 --> 00:16:14,038 1415 | Image two button. 1416 | 1417 | 354 1418 | 00:16:14,107 --> 00:16:17,008 1419 | And neither does that so both of these buttons 1420 | 1421 | 355 1422 | 00:16:17,077 --> 00:16:19,243 1423 | are missing accessibility labels and so 1424 | 1425 | 356 1426 | 00:16:19,312 --> 00:16:21,745 1427 | I have actually no idea what they do. 1428 | 1429 | 357 1430 | 00:16:21,814 --> 00:16:24,949 1431 | And it said image one and two because VoiceOver 1432 | 1433 | 358 1434 | 00:16:25,017 --> 00:16:28,252 1435 | by default when it doesn't have a label on something that 1436 | 1437 | 359 1438 | 00:16:28,321 --> 00:16:32,389 1439 | uses an image like a UIButton, it's going to default to that 1440 | 1441 | 360 1442 | 00:16:32,458 --> 00:16:36,027 1443 | image's filename as a last resort to try and see if it 1444 | 1445 | 361 1446 | 00:16:36,095 --> 00:16:38,829 1447 | can get something meaningful out of what this element is. 1448 | 1449 | 362 1450 | 00:16:38,897 --> 00:16:42,533 1451 | So I need to instead add my own labels to those buttons. 1452 | 1453 | 363 1454 | 00:16:42,602 --> 00:16:47,137 1455 | Trivia, nine trivia. 1456 | 1457 | 364 1458 | 00:16:47,206 --> 00:16:47,939 1459 | Now if I go in here, 1460 | 1461 | 365 1462 | 00:16:48,008 --> 00:16:50,307 1463 | I heard that that had the header trait. 1464 | 1465 | 366 1466 | 00:16:50,376 --> 00:16:52,276 1467 | Add button. 1468 | 1469 | 367 1470 | 00:16:52,344 --> 00:16:53,076 1471 | That has a good label. 1472 | 1473 | 368 1474 | 00:16:53,145 --> 00:16:55,513 1475 | Close button. 1476 | 1477 | 369 1478 | 00:16:55,581 --> 00:16:56,447 1479 | And if I come in here. 1480 | 1481 | 370 1482 | 00:16:56,516 --> 00:16:58,482 1483 | The phrase "Let them eat cake" is commonly 1484 | 1485 | 371 1486 | 00:16:58,551 --> 00:16:59,450 1487 | attributed to whom? 1488 | 1489 | 372 1490 | 00:16:59,519 --> 00:17:00,551 1491 | Queen Marie Antoinette. 1492 | 1493 | 373 1494 | 00:17:00,620 --> 00:17:02,119 1495 | I notice that these two labels, 1496 | 1497 | 374 1498 | 00:17:02,188 --> 00:17:04,388 1499 | even though VoiceOver's picking them up, 1500 | 1501 | 375 1502 | 00:17:04,457 --> 00:17:07,124 1503 | they are picked up as separate elements and so, I don't 1504 | 1505 | 376 1506 | 00:17:07,193 --> 00:17:10,094 1507 | really have any context for how they work together. 1508 | 1509 | 377 1510 | 00:17:10,163 --> 00:17:11,862 1511 | One is the question and one is the answer. 1512 | 1513 | 378 1514 | 00:17:11,930 --> 00:17:14,865 1515 | And it would actually be much better and give VoiceOver 1516 | 1517 | 379 1518 | 00:17:14,934 --> 00:17:17,735 1519 | users much context if I were to group them together. 1520 | 1521 | 380 1522 | 00:17:17,803 --> 00:17:18,636 1523 | That's one element and 1524 | 1525 | 381 1526 | 00:17:18,705 --> 00:17:21,405 1527 | say this is the question, this is the answer. 1528 | 1529 | 382 1530 | 00:17:21,473 --> 00:17:23,107 1531 | So they can actually understand what's happening in 1532 | 1533 | 383 1534 | 00:17:23,175 --> 00:17:24,575 1535 | this interface. 1536 | 1537 | 384 1538 | 00:17:24,644 --> 00:17:27,845 1539 | So I'm gonna go over to some code, to my app. 1540 | 1541 | 385 1542 | 00:17:27,913 --> 00:17:31,282 1543 | The first thing I wanna take care of is that header trade 1544 | 1545 | 386 1546 | 00:17:31,350 --> 00:17:32,016 1547 | cuz it's pretty easy. 1548 | 1549 | 387 1550 | 00:17:32,085 --> 00:17:36,853 1551 | And I have this title label right here, so 1552 | 1553 | 388 1554 | 00:17:36,922 --> 00:17:43,628 1555 | I'm going to say accessibility traits or equals header. 1556 | 1557 | 389 1558 | 00:17:43,696 --> 00:17:46,430 1559 | Now I'm using or equals instead of just explicitly 1560 | 1561 | 390 1562 | 00:17:46,498 --> 00:17:51,101 1563 | equals, because I don't know what the superclass of title 1564 | 1565 | 391 1566 | 00:17:51,170 --> 00:17:54,071 1567 | label, what traits it is added to 1568 | 1569 | 392 1570 | 00:17:54,140 --> 00:17:56,774 1571 | this accessibility element that it wants to have there. 1572 | 1573 | 393 1574 | 00:17:56,843 --> 00:17:58,642 1575 | So I don't wanna override whatever it set. 1576 | 1577 | 394 1578 | 00:17:58,711 --> 00:18:00,044 1579 | I just wanna add my own in. 1580 | 1581 | 395 1582 | 00:18:00,113 --> 00:18:03,114 1583 | So that's what I'm doing there. 1584 | 1585 | 396 1586 | 00:18:03,182 --> 00:18:07,918 1587 | Next, let's look at this cell and the shuffle, and 1588 | 1589 | 397 1590 | 00:18:07,987 --> 00:18:09,453 1591 | delete button. 1592 | 1593 | 398 1594 | 00:18:09,522 --> 00:18:13,357 1595 | So I need to add accessibility labels for 1596 | 1597 | 399 1598 | 00:18:13,426 --> 00:18:15,893 1599 | these and come here and say 1600 | 1601 | 400 1602 | 00:18:15,961 --> 00:18:20,064 1603 | shuffleButton.accessibilityLa- bel. 1604 | 1605 | 401 1606 | 00:18:22,101 --> 00:18:24,701 1607 | Shuffle seems like a reasonable label to give it. 1608 | 1609 | 402 1610 | 00:18:24,770 --> 00:18:29,841 1611 | I'm gonna say delete button equals delete. 1612 | 1613 | 403 1614 | 00:18:31,577 --> 00:18:34,845 1615 | This seems to convey to me what exactly these buttons 1616 | 1617 | 404 1618 | 00:18:34,914 --> 00:18:36,147 1619 | are supposed to do. 1620 | 1621 | 405 1622 | 00:18:36,215 --> 00:18:38,349 1623 | And finally, I'm gonna come to this collection view cell 1624 | 1625 | 406 1626 | 00:18:38,417 --> 00:18:40,518 1627 | to have this question and answer. 1628 | 1629 | 407 1630 | 00:18:40,586 --> 00:18:41,752 1631 | What I'm gonna do here 1632 | 1633 | 408 1634 | 00:18:43,890 --> 00:18:49,393 1635 | is override these methods or these properties. 1636 | 1637 | 409 1638 | 00:18:49,461 --> 00:18:51,095 1639 | So I'm gonna override this accessibility element to 1640 | 1641 | 410 1642 | 00:18:51,164 --> 00:18:53,764 1643 | always return true on the cell itself so 1644 | 1645 | 411 1646 | 00:18:53,832 --> 00:18:56,000 1647 | that the entire cell has the accessibility element 1648 | 1649 | 412 1650 | 00:18:56,068 --> 00:18:57,301 1651 | instead of each individual label. 1652 | 1653 | 413 1654 | 00:18:57,369 --> 00:19:01,471 1655 | I'm gonna set the label to return Question, 1656 | 1657 | 414 1658 | 00:19:01,540 --> 00:19:03,374 1659 | then the question's text. 1660 | 1661 | 415 1662 | 00:19:03,443 --> 00:19:05,543 1663 | Answer, and then the answer's text. 1664 | 1665 | 416 1666 | 00:19:05,611 --> 00:19:07,611 1667 | And I'm going to add the button trait to it because 1668 | 1669 | 417 1670 | 00:19:07,680 --> 00:19:10,414 1671 | that cell is a button where I can go in and edit the card. 1672 | 1673 | 418 1674 | 00:19:11,917 --> 00:19:14,018 1675 | And I need to convey that to the user. 1676 | 1677 | 419 1678 | 00:19:16,255 --> 00:19:17,255 1679 | So let's build this. 1680 | 1681 | 420 1682 | 00:19:19,358 --> 00:19:20,191 1683 | Notecards. 1684 | 1685 | 421 1686 | 00:19:22,795 --> 00:19:26,129 1687 | [SOUND] Notecards, collections heading. 1688 | 1689 | 422 1690 | 00:19:26,198 --> 00:19:27,197 1691 | So I've got the header trait. 1692 | 1693 | 423 1694 | 00:19:27,266 --> 00:19:32,836 1695 | Tab physics shuffle button delete button. 1696 | 1697 | 424 1698 | 00:19:32,905 --> 00:19:34,638 1699 | Both buttons have the right labels. 1700 | 1701 | 425 1702 | 00:19:34,707 --> 00:19:38,942 1703 | Trivia, link trivia, trivia question: the phrase "Let them 1704 | 1705 | 426 1706 | 00:19:39,011 --> 00:19:41,945 1707 | eat cake" is commonly attributed to whom? 1708 | 1709 | 427 1710 | 00:19:42,014 --> 00:19:44,448 1711 | Answer Queen Marie Antoinette. 1712 | 1713 | 428 1714 | 00:19:44,517 --> 00:19:47,417 1715 | So, I've got it behaving exactly as I want it, 1716 | 1717 | 429 1718 | 00:19:47,486 --> 00:19:50,220 1719 | just a few simple lines of code. 1720 | 1721 | 430 1722 | 00:19:50,289 --> 00:19:53,023 1723 | That's just a couple examples of some of the issues that you 1724 | 1725 | 431 1726 | 00:19:53,092 --> 00:19:54,325 1727 | might find in your own apps. 1728 | 1729 | 432 1730 | 00:19:55,528 --> 00:19:58,129 1731 | So I'm gonna go back, to my account. 1732 | 1733 | 433 1734 | 00:20:00,933 --> 00:20:06,069 1735 | I also would like to touch on- VoiceOver off. 1736 | 1737 | 434 1738 | 00:20:06,138 --> 00:20:08,906 1739 | The idea of containers. 1740 | 1741 | 435 1742 | 00:20:08,975 --> 00:20:11,042 1743 | So aside from the attributes. 1744 | 1745 | 436 1746 | 00:20:12,711 --> 00:20:16,113 1747 | We also have the accessibility container protocol. 1748 | 1749 | 437 1750 | 00:20:16,182 --> 00:20:17,648 1751 | So let's say that I am the engineer who has been 1752 | 1753 | 438 1754 | 00:20:17,716 --> 00:20:20,150 1755 | tasked with coding this view in the Calendar app, 1756 | 1757 | 439 1758 | 00:20:20,219 --> 00:20:21,618 1759 | it's the year view. 1760 | 1761 | 440 1762 | 00:20:21,687 --> 00:20:25,156 1763 | And I've decided that I am going to design this view so 1764 | 1765 | 441 1766 | 00:20:25,225 --> 00:20:29,093 1767 | that each week is its own custom UIView. 1768 | 1769 | 442 1770 | 00:20:29,162 --> 00:20:31,561 1771 | And within that UIView's drawRect method, 1772 | 1773 | 443 1774 | 00:20:31,630 --> 00:20:35,232 1775 | I'm going to explicitly draw all the days of the week. 1776 | 1777 | 444 1778 | 00:20:35,301 --> 00:20:38,035 1779 | Now because those views that I've drawn aren't standard 1780 | 1781 | 445 1782 | 00:20:38,104 --> 00:20:39,337 1783 | UIKit views, 1784 | 1785 | 446 1786 | 00:20:39,405 --> 00:20:42,106 1787 | they don't inherit accessibility by default. 1788 | 1789 | 447 1790 | 00:20:42,175 --> 00:20:46,043 1791 | And I don't really have an easy way of setting 1792 | 1793 | 448 1794 | 00:20:46,112 --> 00:20:48,246 1795 | the accessibility properties on each of them. 1796 | 1797 | 449 1798 | 00:20:50,182 --> 00:20:50,948 1799 | But what we'd like for 1800 | 1801 | 450 1802 | 00:20:51,017 --> 00:20:53,984 1803 | them to be is visible to VoiceOver one day at a time. 1804 | 1805 | 451 1806 | 00:20:54,053 --> 00:20:56,920 1807 | And so that I can navigate them day by day and 1808 | 1809 | 452 1810 | 00:20:56,989 --> 00:20:59,656 1811 | get a description for each day at a time, 1812 | 1813 | 453 1814 | 00:20:59,725 --> 00:21:01,458 1815 | rather than each week at a time. 1816 | 1817 | 454 1818 | 00:21:01,527 --> 00:21:04,795 1819 | So I need to do is create a set of accessibility elements 1820 | 1821 | 455 1822 | 00:21:04,864 --> 00:21:07,364 1823 | from scratch that overlay these day views and 1824 | 1825 | 456 1826 | 00:21:07,433 --> 00:21:10,700 1827 | return them as sub-elements of my accessibility, or 1828 | 1829 | 457 1830 | 00:21:10,769 --> 00:21:12,503 1831 | of my week view container. 1832 | 1833 | 458 1834 | 00:21:14,540 --> 00:21:16,573 1835 | So what does that look like in code? 1836 | 1837 | 459 1838 | 00:21:16,642 --> 00:21:19,710 1839 | Well, I have this MyWeekView class. 1840 | 1841 | 460 1842 | 00:21:19,779 --> 00:21:22,913 1843 | Which I'm going to explicitly say is not an accessibility 1844 | 1845 | 461 1846 | 00:21:22,982 --> 00:21:26,150 1847 | element, because I instead want VoiceOver to go through 1848 | 1849 | 462 1850 | 00:21:26,219 --> 00:21:29,320 1851 | and find the sub-elements of it that are accessible. 1852 | 1853 | 463 1854 | 00:21:31,690 --> 00:21:34,625 1855 | And I'm going to create an array of elements that 1856 | 1857 | 464 1858 | 00:21:34,694 --> 00:21:37,661 1859 | are UIAccessibilityElement. 1860 | 1861 | 465 1862 | 00:21:37,730 --> 00:21:39,797 1863 | I'm going to iterate over the days of the week that 1864 | 1865 | 466 1866 | 00:21:39,866 --> 00:21:41,432 1867 | are in that week. 1868 | 1869 | 467 1870 | 00:21:41,501 --> 00:21:44,401 1871 | And for each one I'm going to create an accessibility 1872 | 1873 | 468 1874 | 00:21:44,470 --> 00:21:47,537 1875 | element, with the accessibility container set 1876 | 1877 | 469 1878 | 00:21:47,606 --> 00:21:50,207 1879 | as myself, because I'm the one that contains it. 1880 | 1881 | 470 1882 | 00:21:50,276 --> 00:21:52,776 1883 | I'm going to set its label to be something 1884 | 1885 | 471 1886 | 00:21:52,844 --> 00:21:53,677 1887 | sensible for that day. 1888 | 1889 | 472 1890 | 00:21:53,746 --> 00:21:55,913 1891 | And now I'm going to set this property 1892 | 1893 | 473 1894 | 00:21:55,982 --> 00:21:58,648 1895 | accessibilityFrameInContainer- Space. 1896 | 1897 | 474 1898 | 00:21:58,717 --> 00:22:01,618 1899 | Now, this is crucial because it indicates to VoiceOver 1900 | 1901 | 475 1902 | 00:22:01,687 --> 00:22:04,721 1903 | where the element is on the screen, so that when I'm 1904 | 1905 | 476 1906 | 00:22:04,790 --> 00:22:09,760 1907 | panning around with my finger VoiceOver knows where it is. 1908 | 1909 | 477 1910 | 00:22:09,828 --> 00:22:12,863 1911 | So if my finger is over it, it can describe it to me and 1912 | 1913 | 478 1914 | 00:22:12,931 --> 00:22:14,965 1915 | see that I'm trying to focus on that element. 1916 | 1917 | 479 1918 | 00:22:15,033 --> 00:22:17,368 1919 | It's also what VoiceOver uses to draw the bounding box. 1920 | 1921 | 480 1922 | 00:22:19,805 --> 00:22:23,073 1923 | And in case you couldn't tell this frame should be in 1924 | 1925 | 481 1926 | 00:22:23,142 --> 00:22:24,809 1927 | the coordinate space of its container. 1928 | 1929 | 482 1930 | 00:22:26,278 --> 00:22:30,681 1931 | So once I do that, I'll pan the element to my array of 1932 | 1933 | 483 1934 | 00:22:30,750 --> 00:22:33,650 1935 | accessibility elements, and then, I will set the property 1936 | 1937 | 484 1938 | 00:22:33,719 --> 00:22:37,488 1939 | on MyWeekView accessibility elements to be those elements. 1940 | 1941 | 485 1942 | 00:22:37,556 --> 00:22:41,325 1943 | And what this says is these are all my accessible 1944 | 1945 | 486 1946 | 00:22:41,394 --> 00:22:45,228 1947 | sub-elements and VoiceOver should navigate them 1948 | 1949 | 487 1950 | 00:22:45,297 --> 00:22:48,065 1951 | in the order that I returned them in this array. 1952 | 1953 | 488 1954 | 00:22:49,868 --> 00:22:52,202 1955 | So, let's take a look at adding that to my sample app. 1956 | 1957 | 489 1958 | 00:22:52,271 --> 00:22:55,106 1959 | Let me go back over here. 1960 | 1961 | 490 1962 | 00:22:56,976 --> 00:22:58,008 1963 | And turn the VoiceOver back on. 1964 | 1965 | 491 1966 | 00:22:58,077 --> 00:23:02,479 1967 | VoiceOver on now. 1968 | 1969 | 492 1970 | 00:23:02,548 --> 00:23:03,814 1971 | So you may have wondered 1972 | 1973 | 493 1974 | 00:23:03,883 --> 00:23:05,582 1975 | what this shuffle button actually does. 1976 | 1977 | 494 1978 | 00:23:05,651 --> 00:23:09,286 1979 | Well, it shuffles the cards in the deck and puts them up so 1980 | 1981 | 495 1982 | 00:23:09,354 --> 00:23:09,886 1983 | I can test them. 1984 | 1985 | 496 1986 | 00:23:09,955 --> 00:23:14,257 1987 | Shuffle trivia heading. 1988 | 1989 | 497 1990 | 00:23:14,326 --> 00:23:15,426 1991 | And what I have down here, 1992 | 1993 | 498 1994 | 00:23:15,495 --> 00:23:19,163 1995 | this little circle view is just a little UI to 1996 | 1997 | 499 1998 | 00:23:19,231 --> 00:23:22,099 1999 | indicate to me how far I am through the deck. 2000 | 2001 | 500 2002 | 00:23:22,167 --> 00:23:25,369 2003 | So if I go to the next card for instance- Next question. 2004 | 2005 | 501 2006 | 00:23:25,437 --> 00:23:28,205 2007 | It's increasing because I'm filling out the circle 2008 | 2009 | 502 2010 | 00:23:28,274 --> 00:23:29,974 2011 | with how many cards I've done. 2012 | 2013 | 503 2014 | 00:23:30,042 --> 00:23:31,909 2015 | However if I stay to swipe to it. 2016 | 2017 | 504 2018 | 00:23:31,977 --> 00:23:34,544 2019 | Next question, from next question. 2020 | 2021 | 505 2022 | 00:23:34,613 --> 00:23:36,013 2023 | I hear a bonk and 2024 | 2025 | 506 2026 | 00:23:36,081 --> 00:23:40,083 2027 | try to run my finger I'm also hearing a bonk. 2028 | 2029 | 507 2030 | 00:23:40,152 --> 00:23:43,186 2031 | So what that indicates is that when I swiped and 2032 | 2033 | 508 2034 | 00:23:43,255 --> 00:23:46,590 2035 | did it it means there's nothing left on the screen for 2036 | 2037 | 509 2038 | 00:23:46,658 --> 00:23:49,093 2039 | VoiceOver to swipe to you've hit the last element. 2040 | 2041 | 510 2042 | 00:23:49,161 --> 00:23:50,961 2043 | And when I'm running my finger over it means that there's 2044 | 2045 | 511 2046 | 00:23:51,030 --> 00:23:53,797 2047 | nothing below my finger that is an accessible element. 2048 | 2049 | 512 2050 | 00:23:53,866 --> 00:23:56,900 2051 | But we'd really like that to be accessible to VoiceOver 2052 | 2053 | 513 2054 | 00:23:56,969 --> 00:23:59,837 2055 | users, so they can track their progress through this stack. 2056 | 2057 | 514 2058 | 00:23:59,905 --> 00:24:01,004 2059 | And so we're gonna use 2060 | 2061 | 515 2062 | 00:24:01,073 --> 00:24:05,109 2063 | accessibility elements to accomplish this. 2064 | 2065 | 516 2066 | 00:24:05,178 --> 00:24:07,911 2067 | So I'm gonna come back over to my code. 2068 | 2069 | 517 2070 | 00:24:07,980 --> 00:24:09,913 2071 | I'm gonna go to this ShuffleStatusView, 2072 | 2073 | 518 2074 | 00:24:09,982 --> 00:24:12,616 2075 | which is where I've done the drawing of this circle. 2076 | 2077 | 519 2078 | 00:24:15,454 --> 00:24:16,954 2079 | I'm gonna copy and paste. 2080 | 2081 | 520 2082 | 00:24:22,561 --> 00:24:24,528 2083 | So, what I've done here is I've created 2084 | 2085 | 521 2086 | 00:24:24,597 --> 00:24:27,731 2087 | an AccessibilityElement with myself as the container. 2088 | 2089 | 522 2090 | 00:24:27,800 --> 00:24:30,901 2091 | I set its label to be card, whatever the current card 2092 | 2093 | 523 2094 | 00:24:30,970 --> 00:24:33,937 2095 | number is, of the total amount of cards. 2096 | 2097 | 524 2098 | 00:24:34,006 --> 00:24:36,072 2099 | I've set its accessibilityFrame 2100 | 2101 | 525 2102 | 00:24:36,141 --> 00:24:37,875 2103 | to be the frame of the circle. 2104 | 2105 | 526 2106 | 00:24:37,944 --> 00:24:40,411 2107 | And in my AccessibilityElements because 2108 | 2109 | 527 2110 | 00:24:40,479 --> 00:24:41,412 2111 | the previous and 2112 | 2113 | 528 2114 | 00:24:41,480 --> 00:24:44,114 2115 | next button are also part of the StatusView, 2116 | 2117 | 529 2118 | 00:24:44,183 --> 00:24:47,083 2119 | I don't want them to be left out from VoiceOver. 2120 | 2121 | 530 2122 | 00:24:47,152 --> 00:24:48,885 2123 | So I'm gonna also include those in my 2124 | 2125 | 531 2126 | 00:24:48,954 --> 00:24:51,621 2127 | AccessibilityElements along with the new element that 2128 | 2129 | 532 2130 | 00:24:51,690 --> 00:24:52,656 2131 | I've just created. 2132 | 2133 | 533 2134 | 00:24:52,725 --> 00:24:55,425 2135 | So let's see if that worked. 2136 | 2137 | 534 2138 | 00:24:55,494 --> 00:24:58,329 2139 | NoteCards, NoteCards, collect, 2140 | 2141 | 535 2142 | 00:24:58,397 --> 00:25:03,100 2143 | shuffle button, shuffle, trivia, previous question. 2144 | 2145 | 536 2146 | 00:25:03,169 --> 00:25:04,368 2147 | I've got my previous button. 2148 | 2149 | 537 2150 | 00:25:07,373 --> 00:25:12,175 2151 | Next question, problem, part one of nine. 2152 | 2153 | 538 2154 | 00:25:12,244 --> 00:25:14,211 2155 | So now it's become accessible. 2156 | 2157 | 539 2158 | 00:25:14,279 --> 00:25:18,048 2159 | And pretty easy, not too many lines of code. 2160 | 2161 | 540 2162 | 00:25:18,116 --> 00:25:19,383 2163 | We can go back. 2164 | 2165 | 541 2166 | 00:25:21,687 --> 00:25:24,321 2167 | So I'd like to change gears for a minute, and 2168 | 2169 | 542 2170 | 00:25:24,390 --> 00:25:28,792 2171 | talk a little bit about some visual accommodations that you 2172 | 2173 | 543 2174 | 00:25:28,860 --> 00:25:30,594 2175 | should be aware of when you're designing your app. 2176 | 2177 | 544 2178 | 00:25:31,897 --> 00:25:34,364 2179 | So the first thing is color. 2180 | 2181 | 545 2182 | 00:25:34,433 --> 00:25:37,701 2183 | Color is a useful means of differentiating information, 2184 | 2185 | 546 2186 | 00:25:37,769 --> 00:25:40,537 2187 | but if it's the sole differentiator 2188 | 2189 | 547 2190 | 00:25:40,606 --> 00:25:43,139 2191 | then users who are colorblind won't be able to fully 2192 | 2193 | 548 2194 | 00:25:43,208 --> 00:25:44,974 2195 | understand the user interface. 2196 | 2197 | 549 2198 | 00:25:45,043 --> 00:25:47,644 2199 | So, take for example this view in Mail. 2200 | 2201 | 550 2202 | 00:25:47,712 --> 00:25:50,714 2203 | Our designers wanted the unread and 2204 | 2205 | 551 2206 | 00:25:50,783 --> 00:25:54,184 2207 | flagged messages to be differentiated apart from each 2208 | 2209 | 552 2210 | 00:25:54,253 --> 00:25:58,288 2211 | other with circle views that are of a particular color. 2212 | 2213 | 553 2214 | 00:25:58,356 --> 00:26:01,691 2215 | But to a colorblind user, they can't tell the difference. 2216 | 2217 | 554 2218 | 00:26:01,760 --> 00:26:05,695 2219 | So what we did, is we added a setting in Mail so that you 2220 | 2221 | 555 2222 | 00:26:05,764 --> 00:26:10,533 2223 | can change flag messages to use shape and color. 2224 | 2225 | 556 2226 | 00:26:10,602 --> 00:26:14,204 2227 | And what this does is it uses both color and shape to convey 2228 | 2229 | 557 2230 | 00:26:14,272 --> 00:26:16,906 2231 | meaning so that no user has to be left out. 2232 | 2233 | 558 2234 | 00:26:16,975 --> 00:26:19,576 2235 | So, if you're writing an app that uses color to 2236 | 2237 | 559 2238 | 00:26:19,644 --> 00:26:20,343 2239 | convey meaning, 2240 | 2241 | 560 2242 | 00:26:20,412 --> 00:26:22,046 2243 | then you should consider doing something similar. 2244 | 2245 | 561 2246 | 00:26:23,949 --> 00:26:26,850 2247 | Now color can also be too bright, distracting, and 2248 | 2249 | 562 2250 | 00:26:26,919 --> 00:26:28,585 2251 | contrast and transparency and 2252 | 2253 | 563 2254 | 00:26:28,653 --> 00:26:32,222 2255 | blurring can all have negative impacts on legibility for 2256 | 2257 | 564 2258 | 00:26:32,291 --> 00:26:34,891 2259 | people that have certain vision conditions. 2260 | 2261 | 565 2262 | 00:26:34,960 --> 00:26:37,427 2263 | This is the default control center on iOS and 2264 | 2265 | 566 2266 | 00:26:37,496 --> 00:26:40,330 2267 | as you can see color bleeds through from behind and 2268 | 2269 | 567 2270 | 00:26:40,399 --> 00:26:43,233 2271 | is blurred by the control center's background. 2272 | 2273 | 568 2274 | 00:26:43,302 --> 00:26:45,402 2275 | But if I turn on the darken colors and 2276 | 2277 | 569 2278 | 00:26:45,471 --> 00:26:47,370 2279 | reduce transparency setting, 2280 | 2281 | 570 2282 | 00:26:47,439 --> 00:26:51,941 2283 | we no longer get those colors bleeding through from behind. 2284 | 2285 | 571 2286 | 00:26:52,010 --> 00:26:53,777 2287 | And the contrast between the colors and 2288 | 2289 | 572 2290 | 00:26:53,846 --> 00:26:55,846 2291 | the lighter background is much more stark, 2292 | 2293 | 573 2294 | 00:26:55,914 --> 00:26:58,482 2295 | which means that it's a lot more readable to those users 2296 | 2297 | 574 2298 | 00:26:58,550 --> 00:27:00,850 2299 | that have those vision conditions. 2300 | 2301 | 575 2302 | 00:27:00,919 --> 00:27:03,854 2303 | So you can check whether things like these are enabled 2304 | 2305 | 576 2306 | 00:27:03,922 --> 00:27:06,523 2307 | through these two UIKit functions. 2308 | 2309 | 577 2310 | 00:27:06,591 --> 00:27:09,693 2311 | And this way, you can adapt your interface to the needs 2312 | 2313 | 578 2314 | 00:27:09,761 --> 00:27:12,496 2315 | of your users rather than having to try and create 2316 | 2317 | 579 2318 | 00:27:12,564 --> 00:27:16,066 2319 | some design that, from the get go, applies to everybody. 2320 | 2321 | 580 2322 | 00:27:18,503 --> 00:27:21,071 2323 | Now for this next one, I'm going to say that anybody in 2324 | 2325 | 581 2326 | 00:27:21,140 --> 00:27:23,840 2327 | the room who gets motion sick from phone animation should 2328 | 2329 | 582 2330 | 00:27:23,909 --> 00:27:26,042 2331 | probably look away for a second. 2332 | 2333 | 583 2334 | 00:27:26,111 --> 00:27:27,911 2335 | Device is gonna show these standard 2336 | 2337 | 584 2338 | 00:27:27,979 --> 00:27:30,047 2339 | app opening animation. 2340 | 2341 | 585 2342 | 00:27:30,116 --> 00:27:32,282 2343 | So you can see that when I tap on music, 2344 | 2345 | 586 2346 | 00:27:32,351 --> 00:27:34,884 2347 | the app expands from its place on the dock, and 2348 | 2349 | 587 2350 | 00:27:34,953 --> 00:27:38,154 2351 | the screen swoops in to center on it. 2352 | 2353 | 588 2354 | 00:27:38,223 --> 00:27:40,623 2355 | But if I turn on the reduce motion setting, 2356 | 2357 | 589 2358 | 00:27:40,692 --> 00:27:43,193 2359 | I instead get this cross fade. 2360 | 2361 | 590 2362 | 00:27:43,262 --> 00:27:46,864 2363 | So there are certain people with vestibular disorders 2364 | 2365 | 591 2366 | 00:27:46,932 --> 00:27:50,934 2367 | that can be triggered by complex motion which most 2368 | 2369 | 592 2370 | 00:27:51,002 --> 00:27:54,271 2371 | often results in things like nausea and dizziness. 2372 | 2373 | 593 2374 | 00:27:54,339 --> 00:27:57,407 2375 | So you should be mindful of any animations in your app, 2376 | 2377 | 594 2378 | 00:27:57,476 --> 00:28:00,544 2379 | particularly if they cause a lot of movement or 2380 | 2381 | 595 2382 | 00:28:00,613 --> 00:28:03,113 2383 | are unexpected in some way. 2384 | 2385 | 596 2386 | 00:28:03,181 --> 00:28:05,715 2387 | You should consider enabling an alternative 2388 | 2389 | 597 2390 | 00:28:05,784 --> 00:28:08,852 2391 | animation when this setting is enabled which you can check 2392 | 2393 | 598 2394 | 00:28:08,920 --> 00:28:09,953 2395 | through a similar UIKit function. 2396 | 2397 | 599 2398 | 00:28:12,724 --> 00:28:14,391 2399 | Now the final visual accommodation that I'd like 2400 | 2401 | 600 2402 | 00:28:14,460 --> 00:28:17,594 2403 | to talk about today relates to typography, and 2404 | 2405 | 601 2406 | 00:28:17,663 --> 00:28:20,430 2407 | it's dynamic type which is a feature in iOS that allows 2408 | 2409 | 602 2410 | 00:28:20,499 --> 00:28:22,299 2411 | a user to scale their font. 2412 | 2413 | 603 2414 | 00:28:22,368 --> 00:28:24,535 2415 | And I believe you touched on it very briefly. 2416 | 2417 | 604 2418 | 00:28:26,138 --> 00:28:29,339 2419 | There are many users out there who struggle to use devices 2420 | 2421 | 605 2422 | 00:28:29,407 --> 00:28:32,442 2423 | because the type is so small, or so thin that it ends up 2424 | 2425 | 606 2426 | 00:28:32,511 --> 00:28:35,111 2427 | blurring together, and they can't read it. 2428 | 2429 | 607 2430 | 00:28:35,180 --> 00:28:38,582 2431 | And this applies to people with low vision conditions, 2432 | 2433 | 608 2434 | 00:28:38,651 --> 00:28:41,718 2435 | but it also applies to people whose vision is simply 2436 | 2437 | 609 2438 | 00:28:41,787 --> 00:28:43,420 2439 | deteriorated from age, or 2440 | 2441 | 610 2442 | 00:28:43,489 --> 00:28:46,223 2443 | people who simply need to wear glasses. 2444 | 2445 | 611 2446 | 00:28:46,292 --> 00:28:48,925 2447 | So, Dynamic Type is meant to help adapt these 2448 | 2449 | 612 2450 | 00:28:48,994 --> 00:28:50,327 2451 | people's devices to their needs. 2452 | 2453 | 613 2454 | 00:28:50,396 --> 00:28:54,865 2455 | Now on the left you see the standard Mail inbox interface 2456 | 2457 | 614 2458 | 00:28:54,933 --> 00:28:55,599 2459 | with the default text. 2460 | 2461 | 615 2462 | 00:28:55,667 --> 00:28:58,968 2463 | And on the right I've scaled it up several levels. 2464 | 2465 | 616 2466 | 00:28:59,037 --> 00:28:59,836 2467 | And as you can see, 2468 | 2469 | 617 2470 | 00:28:59,904 --> 00:29:02,372 2471 | the interface has adapted itself and the layout as well 2472 | 2473 | 618 2474 | 00:29:02,441 --> 00:29:05,341 2475 | to accommodate the larger text size. 2476 | 2477 | 619 2478 | 00:29:05,410 --> 00:29:07,811 2479 | The most common way that you're gonna get this behavior 2480 | 2481 | 620 2482 | 00:29:07,880 --> 00:29:11,882 2483 | is by using this method preferredFont(forTextStyle. 2484 | 2485 | 621 2486 | 00:29:11,950 --> 00:29:13,751 2487 | which is a class method on UIFont. 2488 | 2489 | 622 2490 | 00:29:14,986 --> 00:29:17,220 2491 | When you set the font of a UILabel or 2492 | 2493 | 623 2494 | 00:29:17,288 --> 00:29:20,958 2495 | UITextView, using this method, the font will be scaled to 2496 | 2497 | 624 2498 | 00:29:21,026 --> 00:29:24,060 2499 | the user's preference using the system font. 2500 | 2501 | 625 2502 | 00:29:24,129 --> 00:29:26,730 2503 | And what you do is you pass through a TextStyle, which 2504 | 2505 | 626 2506 | 00:29:26,798 --> 00:29:29,565 2507 | indicates what the general purpose of the text is. 2508 | 2509 | 627 2510 | 00:29:29,634 --> 00:29:33,036 2511 | So titles are large and body is a more standard size. 2512 | 2513 | 628 2514 | 00:29:33,105 --> 00:29:36,073 2515 | There are these eight distinct styles that you can pass this 2516 | 2517 | 629 2518 | 00:29:36,141 --> 00:29:39,009 2519 | method and each of them have pre-defined point sizes and 2520 | 2521 | 630 2522 | 00:29:39,077 --> 00:29:40,310 2523 | weight sizes to them. 2524 | 2525 | 631 2526 | 00:29:42,414 --> 00:29:45,215 2527 | So, let's look at some code where I used that. 2528 | 2529 | 632 2530 | 00:29:45,284 --> 00:29:48,118 2531 | So let's say I have MyLabelView which is 2532 | 2533 | 633 2534 | 00:29:48,187 --> 00:29:50,987 2535 | a custom UIView, and it has a sub label or 2536 | 2537 | 634 2538 | 00:29:51,056 --> 00:29:53,556 2539 | a sub view that is of class UILabel. 2540 | 2541 | 635 2542 | 00:29:53,625 --> 00:29:56,293 2543 | Now in my init method, I'm going to init that label. 2544 | 2545 | 636 2546 | 00:29:56,362 --> 00:29:58,828 2547 | Now, I'm gonna set its number of lines to be 0. 2548 | 2549 | 637 2550 | 00:29:58,897 --> 00:30:02,232 2551 | And what this indicates to the UILabel, is that it doesn't 2552 | 2553 | 638 2554 | 00:30:02,301 --> 00:30:05,201 2555 | need to be limited to any specific number of lines. 2556 | 2557 | 639 2558 | 00:30:05,270 --> 00:30:07,570 2559 | It can actually grow to an unbounded number of lines. 2560 | 2561 | 640 2562 | 00:30:07,639 --> 00:30:10,440 2563 | Which is important when I'm growing the text size because 2564 | 2565 | 641 2566 | 00:30:10,508 --> 00:30:13,376 2567 | as I get bigger and bigger, it's likelier, it's more and 2568 | 2569 | 642 2570 | 00:30:13,445 --> 00:30:16,012 2571 | more likely that I'm not gonna be able to fit whatever 2572 | 2573 | 643 2574 | 00:30:16,081 --> 00:30:17,581 2575 | text I have on a single line. 2576 | 2577 | 644 2578 | 00:30:19,985 --> 00:30:23,720 2579 | Now I also need to set adjustsFontForContentSizeCate- 2580 | 2581 | 645 2582 | 00:30:23,788 --> 00:30:27,524 2583 | gory to be true, and what this does is it tells the label 2584 | 2585 | 646 2586 | 00:30:27,592 --> 00:30:30,927 2587 | that when the user has changed their font size and 2588 | 2589 | 647 2590 | 00:30:30,996 --> 00:30:35,565 2591 | my app is opened in the background, I need to update. 2592 | 2593 | 648 2594 | 00:30:35,633 --> 00:30:38,569 2595 | The label needs to update itself to match whatever 2596 | 2597 | 649 2598 | 00:30:38,637 --> 00:30:41,872 2599 | the new font that I've changed my size to is. 2600 | 2601 | 650 2602 | 00:30:43,842 --> 00:30:46,009 2603 | And then I am going to use that preferredFont 2604 | 2605 | 651 2606 | 00:30:46,078 --> 00:30:46,843 2607 | forTextStyle. 2608 | 2609 | 652 2610 | 00:30:46,912 --> 00:30:48,744 2611 | Probably just gonna use body, 2612 | 2613 | 653 2614 | 00:30:48,813 --> 00:30:52,482 2615 | because it's standard in this case and then add my subview. 2616 | 2617 | 654 2618 | 00:30:52,550 --> 00:30:55,485 2619 | Now, what do you do in the case where you wanna use 2620 | 2621 | 655 2622 | 00:30:55,554 --> 00:30:58,355 2623 | a custom point size when you're at the default 2624 | 2625 | 656 2626 | 00:30:58,423 --> 00:30:59,156 2627 | text size? 2628 | 2629 | 657 2630 | 00:30:59,225 --> 00:31:01,457 2631 | Or you even wanna use a custom font but 2632 | 2633 | 658 2634 | 00:31:01,526 --> 00:31:02,926 2635 | you still wanna support dynamic type. 2636 | 2637 | 659 2638 | 00:31:04,229 --> 00:31:05,762 2639 | I did this in my own sample app, 2640 | 2641 | 660 2642 | 00:31:05,831 --> 00:31:08,097 2643 | it uses the boldSystemFontOfSize method, 2644 | 2645 | 661 2646 | 00:31:08,166 --> 00:31:10,600 2647 | which is not going to automatically be scaled to 2648 | 2649 | 662 2650 | 00:31:10,668 --> 00:31:12,135 2651 | the user's preference. 2652 | 2653 | 663 2654 | 00:31:13,705 --> 00:31:16,973 2655 | So, what I'm gonna do is I'm going to take advantage of 2656 | 2657 | 664 2658 | 00:31:17,042 --> 00:31:21,879 2659 | this property on UIApplication preferredContestSizeCategory. 2660 | 2661 | 665 2662 | 00:31:22,848 --> 00:31:24,113 2663 | And what this tells me, 2664 | 2665 | 666 2666 | 00:31:24,182 --> 00:31:26,450 2667 | is what the user has currently set their font to. 2668 | 2669 | 667 2670 | 00:31:28,653 --> 00:31:35,124 2671 | Now there are seven distinct size categories at default. 2672 | 2673 | 668 2674 | 00:31:35,193 --> 00:31:37,661 2675 | The large size is the default size on iOS. 2676 | 2677 | 669 2678 | 00:31:39,031 --> 00:31:42,899 2679 | But there are also five more accessibility sizes and 2680 | 2681 | 670 2682 | 00:31:42,968 --> 00:31:46,536 2683 | located in accessibility settings that can make 2684 | 2685 | 671 2686 | 00:31:46,605 --> 00:31:48,872 2687 | the text really, really big. 2688 | 2689 | 672 2690 | 00:31:48,940 --> 00:31:52,409 2691 | So what would my code for something like this look like? 2692 | 2693 | 673 2694 | 00:31:52,478 --> 00:31:56,045 2695 | Well, I'd probably create some sort of NSObject like 2696 | 2697 | 674 2698 | 00:31:56,114 --> 00:31:59,248 2699 | MyFontManager, that will be an object that will manage all 2700 | 2701 | 675 2702 | 00:31:59,317 --> 00:32:02,219 2703 | the fonts for my app so that I can run everything through it. 2704 | 2705 | 676 2706 | 00:32:03,656 --> 00:32:06,289 2707 | And then I'd have some method like this, 2708 | 2709 | 677 2710 | 00:32:06,358 --> 00:32:10,927 2711 | where I'd use a fontSize variable and write a switch 2712 | 2713 | 678 2714 | 00:32:10,996 --> 00:32:14,197 2715 | statement that changes the value of the fontSize based 2716 | 2717 | 679 2718 | 00:32:14,266 --> 00:32:17,734 2719 | on what my sizeCategory is set to, to some sensible value. 2720 | 2721 | 680 2722 | 00:32:17,802 --> 00:32:20,303 2723 | I mean to do that for every case. 2724 | 2725 | 681 2726 | 00:32:20,372 --> 00:32:22,605 2727 | And then I'm gonna use whatever the method 2728 | 2729 | 682 2730 | 00:32:22,674 --> 00:32:25,375 2731 | I was going to use before, like systemFont(ofSize: or 2732 | 2733 | 683 2734 | 00:32:25,444 --> 00:32:28,445 2735 | my custom font or boldSystemFont(ofSize: 2736 | 2737 | 684 2738 | 00:32:28,514 --> 00:32:31,281 2739 | using whatever size I determined was appropriate for 2740 | 2741 | 685 2742 | 00:32:31,350 --> 00:32:32,482 2743 | the current size class. 2744 | 2745 | 686 2746 | 00:32:34,252 --> 00:32:39,556 2747 | Now it's also important that I register myself to listen for 2748 | 2749 | 687 2750 | 00:32:39,624 --> 00:32:42,125 2751 | the UIContentSizeCategoryDidChange 2752 | 2753 | 688 2754 | 00:32:42,194 --> 00:32:42,992 2755 | notification. 2756 | 2757 | 689 2758 | 00:32:43,061 --> 00:32:45,428 2759 | Because I'm no longer using that preferred font for 2760 | 2761 | 690 2762 | 00:32:45,496 --> 00:32:48,831 2763 | text style method so all my views aren't gonna know that 2764 | 2765 | 691 2766 | 00:32:48,900 --> 00:32:51,968 2767 | they need to update themselves when the font size changes. 2768 | 2769 | 692 2770 | 00:32:52,036 --> 00:32:55,372 2771 | So I'm gonna register myself as an observer on this 2772 | 2773 | 693 2774 | 00:32:55,440 --> 00:32:59,408 2775 | notification and just write a method that updates 2776 | 2777 | 694 2778 | 00:32:59,477 --> 00:33:01,078 2779 | my fonts in some sort of sensible way. 2780 | 2781 | 695 2782 | 00:33:02,814 --> 00:33:05,749 2783 | So let's move for the final time to my demo app, 2784 | 2785 | 696 2786 | 00:33:05,818 --> 00:33:08,185 2787 | and see what it's like with dynamic type. 2788 | 2789 | 697 2790 | 00:33:10,088 --> 00:33:13,090 2791 | Now for this, I'm actually gonna exit full screen. 2792 | 2793 | 698 2794 | 00:33:17,162 --> 00:33:21,698 2795 | And I'm going to bring up this tool in Xcode called 2796 | 2797 | 699 2798 | 00:33:21,767 --> 00:33:24,668 2799 | the Accessibility Inspector. 2800 | 2801 | 700 2802 | 00:33:24,736 --> 00:33:27,870 2803 | Now this is a super useful tool that I don't really have 2804 | 2805 | 701 2806 | 00:33:27,939 --> 00:33:31,040 2807 | time to go in to today about how you can use it. 2808 | 2809 | 702 2810 | 00:33:31,109 --> 00:33:34,477 2811 | But, it's an app that's built into Xcode that allows you to 2812 | 2813 | 703 2814 | 00:33:34,546 --> 00:33:36,213 2815 | audit your own application for 2816 | 2817 | 704 2818 | 00:33:36,281 --> 00:33:38,314 2819 | all of its accessibility problems. 2820 | 2821 | 705 2822 | 00:33:38,383 --> 00:33:41,284 2823 | And there's a great video from last year's WWDC that you can 2824 | 2825 | 706 2826 | 00:33:41,353 --> 00:33:43,787 2827 | find on the developer website to go through exactly 2828 | 2829 | 707 2830 | 00:33:43,855 --> 00:33:44,887 2831 | how you used this app, 2832 | 2833 | 708 2834 | 00:33:44,956 --> 00:33:48,558 2835 | which I will have a link to at the end of the presentation. 2836 | 2837 | 709 2838 | 00:33:48,627 --> 00:33:51,427 2839 | But for now, I'm going to attach to my phone and 2840 | 2841 | 710 2842 | 00:33:51,496 --> 00:33:54,531 2843 | simply use it to change the font size dynamically so 2844 | 2845 | 711 2846 | 00:33:54,599 --> 00:33:57,867 2847 | that you can see what happens on the screen, what I do. 2848 | 2849 | 712 2850 | 00:33:57,936 --> 00:34:00,704 2851 | So I'm gonna come to my stack of cards, and 2852 | 2853 | 713 2854 | 00:34:00,773 --> 00:34:04,374 2855 | I can see that the number of 2856 | 2857 | 714 2858 | 00:34:04,443 --> 00:34:08,144 2859 | lines is already set to zero because this label is growing 2860 | 2861 | 715 2862 | 00:34:08,213 --> 00:34:11,380 2863 | based on how much text I have there as I want it to. 2864 | 2865 | 716 2866 | 00:34:11,449 --> 00:34:12,748 2867 | But we're gonna see, 2868 | 2869 | 717 2870 | 00:34:12,817 --> 00:34:15,518 2871 | is as I change this value here with the slider, 2872 | 2873 | 718 2874 | 00:34:15,587 --> 00:34:18,622 2875 | my text isn't updating at all, the content size. 2876 | 2877 | 719 2878 | 00:34:18,690 --> 00:34:19,723 2879 | So something is wrong. 2880 | 2881 | 720 2882 | 00:34:20,892 --> 00:34:24,194 2883 | So I'm gonna come over here to MyFontManager. 2884 | 2885 | 721 2886 | 00:34:28,066 --> 00:34:29,832 2887 | And I'm gonna go look at my appFont method and 2888 | 2889 | 722 2890 | 00:34:29,901 --> 00:34:31,468 2891 | I see the problem immediately. 2892 | 2893 | 723 2894 | 00:34:31,536 --> 00:34:34,504 2895 | I'm not adapting myself to what the preferred contentSize 2896 | 2897 | 724 2898 | 00:34:34,573 --> 00:34:36,673 2899 | is and I'm not using the preferred font for 2900 | 2901 | 725 2902 | 00:34:36,742 --> 00:34:37,841 2903 | text style method. 2904 | 2905 | 726 2906 | 00:34:46,785 --> 00:34:51,420 2907 | So I'm gonna replace that, With this, 2908 | 2909 | 727 2910 | 00:34:51,489 --> 00:34:53,456 2911 | which takes this size and, 2912 | 2913 | 728 2914 | 00:34:53,524 --> 00:34:57,827 2915 | based on some multiplier that I decided looks good. 2916 | 2917 | 729 2918 | 00:34:57,896 --> 00:35:00,696 2919 | I'm going to modify what the font size is, 2920 | 2921 | 730 2922 | 00:35:00,765 --> 00:35:02,632 2923 | based on the SizeCategory. 2924 | 2925 | 731 2926 | 00:35:02,701 --> 00:35:04,534 2927 | And I'm going to return that using my 2928 | 2929 | 732 2930 | 00:35:04,603 --> 00:35:06,135 2931 | boldSystemFont(ofSize:. 2932 | 2933 | 733 2934 | 00:35:06,204 --> 00:35:07,204 2935 | Now, like I said, 2936 | 2937 | 734 2938 | 00:35:07,272 --> 00:35:09,940 2939 | I also need to register for that notification, so 2940 | 2941 | 735 2942 | 00:35:10,008 --> 00:35:12,775 2943 | that my view actually updates itself in real time. 2944 | 2945 | 736 2946 | 00:35:12,844 --> 00:35:14,177 2947 | So we're gonna go to that cell. 2948 | 2949 | 737 2950 | 00:35:16,448 --> 00:35:19,849 2951 | And I'm going to add myself as an observer, 2952 | 2953 | 738 2954 | 00:35:19,917 --> 00:35:21,785 2955 | not to put that there. 2956 | 2957 | 739 2958 | 00:35:27,426 --> 00:35:29,325 2959 | And in my contentSizeCategoryChange 2960 | 2961 | 740 2962 | 00:35:29,394 --> 00:35:32,129 2963 | method, I'm just going to update the fonts here. 2964 | 2965 | 741 2966 | 00:35:37,235 --> 00:35:39,136 2967 | And that should work, hopefully, so let's rebuild. 2968 | 2969 | 742 2970 | 00:35:41,172 --> 00:35:42,405 2971 | So I'm gonna come back over here. 2972 | 2973 | 743 2974 | 00:35:44,309 --> 00:35:45,808 2975 | I'm gonna open this and 2976 | 2977 | 744 2978 | 00:35:45,877 --> 00:35:49,545 2979 | I'm gonna bring up my Accessibility Inspector again. 2980 | 2981 | 745 2982 | 00:35:49,614 --> 00:35:52,849 2983 | And now as I change the size you see that it's growing 2984 | 2985 | 746 2986 | 00:35:52,918 --> 00:35:57,486 2987 | bigger, and bigger, until it's real, real big. 2988 | 2989 | 747 2990 | 00:35:57,555 --> 00:36:00,223 2991 | And everything works exactly as I was expecting it to. 2992 | 2993 | 748 2994 | 00:36:00,292 --> 00:36:02,759 2995 | And, not a huge change. 2996 | 2997 | 749 2998 | 00:36:02,828 --> 00:36:07,630 2999 | So, I've covered several ways that you as a develop, 3000 | 3001 | 750 3002 | 00:36:07,699 --> 00:36:11,901 3003 | developer can make your app more accessible to people. 3004 | 3005 | 751 3006 | 00:36:11,969 --> 00:36:14,937 3007 | This can actually be a great way to get your app featured 3008 | 3009 | 752 3010 | 00:36:15,006 --> 00:36:15,872 3011 | on the store. 3012 | 3013 | 753 3014 | 00:36:15,941 --> 00:36:18,608 3015 | So we have sections that are devoted specifically to 3016 | 3017 | 754 3018 | 00:36:18,677 --> 00:36:20,409 3019 | general purpose applications. 3020 | 3021 | 755 3022 | 00:36:20,478 --> 00:36:21,344 3023 | That we think we've done 3024 | 3025 | 756 3026 | 00:36:21,413 --> 00:36:23,212 3027 | a good job with their accessibility. 3028 | 3029 | 757 3030 | 00:36:23,281 --> 00:36:25,715 3031 | For instance, this collection of apps that 3032 | 3033 | 758 3034 | 00:36:25,784 --> 00:36:28,117 3035 | are good to use with VoiceOver. 3036 | 3037 | 759 3038 | 00:36:28,186 --> 00:36:31,554 3039 | Beyond making your own app accessible, 3040 | 3041 | 760 3042 | 00:36:31,622 --> 00:36:35,691 3043 | there's also a whole set of apps that are aimed at 3044 | 3045 | 761 3046 | 00:36:35,760 --> 00:36:38,628 3047 | accessible users specifically. 3048 | 3049 | 762 3050 | 00:36:38,696 --> 00:36:41,665 3051 | So, I'd like to run through a few of those examples with 3052 | 3053 | 763 3054 | 00:36:41,733 --> 00:36:43,299 3055 | you today. 3056 | 3057 | 764 3058 | 00:36:43,368 --> 00:36:47,237 3059 | We have knfbReader, which is an app that scans 3060 | 3061 | 765 3062 | 00:36:47,305 --> 00:36:50,507 3063 | text in real time, and let's users who are low vision or 3064 | 3065 | 766 3066 | 00:36:50,576 --> 00:36:52,975 3067 | blind interact with things like paper or 3068 | 3069 | 767 3070 | 00:36:53,044 --> 00:36:54,811 3071 | a restaurant menu, so they can read it. 3072 | 3073 | 768 3074 | 00:36:56,348 --> 00:36:59,783 3075 | We have an app like Ava, which transcribes conversations in 3076 | 3077 | 769 3078 | 00:36:59,851 --> 00:37:02,686 3079 | real time, so that a user who's hard of hearing or 3080 | 3081 | 770 3082 | 00:37:02,754 --> 00:37:05,454 3083 | even deaf, can follow a conversation easier. 3084 | 3085 | 771 3086 | 00:37:05,523 --> 00:37:08,024 3087 | And this includes a feature that actually breaks 3088 | 3089 | 772 3090 | 00:37:08,093 --> 00:37:10,860 3091 | conversation down, differentiating the different 3092 | 3093 | 773 3094 | 00:37:10,929 --> 00:37:13,530 3095 | speakers by color so I can tell who is speaking. 3096 | 3097 | 774 3098 | 00:37:15,300 --> 00:37:18,034 3099 | And finally we have this app, Children with Autism, 3100 | 3101 | 775 3102 | 00:37:18,102 --> 00:37:20,537 3103 | which is a visual scheduler built specifically for 3104 | 3105 | 776 3106 | 00:37:20,605 --> 00:37:22,471 3107 | children with autism. 3108 | 3109 | 777 3110 | 00:37:22,540 --> 00:37:25,374 3111 | And allows them to be more independent and 3112 | 3113 | 778 3114 | 00:37:25,443 --> 00:37:27,676 3115 | understand what they need to do, 3116 | 3117 | 779 3118 | 00:37:27,745 --> 00:37:31,548 3119 | during their day as they are going along, task by task. 3120 | 3121 | 780 3122 | 00:37:31,617 --> 00:37:34,283 3123 | So these are just a couple of examples. 3124 | 3125 | 781 3126 | 00:37:34,352 --> 00:37:37,686 3127 | And you may wonder, why am I telling you about this? 3128 | 3129 | 782 3130 | 00:37:37,755 --> 00:37:42,425 3131 | Well, basically, if you're looking for a possible idea 3132 | 3133 | 783 3134 | 00:37:42,494 --> 00:37:44,994 3135 | for a class of what you might want to work on. 3136 | 3137 | 784 3138 | 00:37:45,062 --> 00:37:48,231 3139 | Or the next great thing that you think you wanna work on, 3140 | 3141 | 785 3142 | 00:37:48,300 --> 00:37:51,534 3143 | I encourage you to check out some of the work that's being 3144 | 3145 | 786 3146 | 00:37:51,603 --> 00:37:54,837 3147 | done in accessibility because there are a lot of problems 3148 | 3149 | 787 3150 | 00:37:54,906 --> 00:37:58,575 3151 | that still aren't solved, and that are worthwhile to tackle. 3152 | 3153 | 788 3154 | 00:37:58,643 --> 00:38:00,476 3155 | Now, I've covered a lot today and 3156 | 3157 | 789 3158 | 00:38:00,545 --> 00:38:03,379 3159 | there's more that you can find on the web. 3160 | 3161 | 790 3162 | 00:38:03,448 --> 00:38:06,682 3163 | These are a couple of links to some resources that you might 3164 | 3165 | 791 3166 | 00:38:06,751 --> 00:38:09,318 3167 | find useful, including some of the things that I spoke about 3168 | 3169 | 792 3170 | 00:38:09,387 --> 00:38:10,720 3171 | during the presentation today. 3172 | 3173 | 793 3174 | 00:38:12,390 --> 00:38:14,324 3175 | I would encourage you to check out some of these WWDC videos, 3176 | 3177 | 794 3178 | 00:38:14,325 --> 00:38:16,259 3179 | In particular, 3180 | 3181 | 795 3182 | 00:38:16,328 --> 00:38:18,628 3183 | including the one on inclusive app design, 3184 | 3185 | 796 3186 | 00:38:18,697 --> 00:38:21,531 3187 | which is a whole session on design, focused on 3188 | 3189 | 797 3190 | 00:38:21,599 --> 00:38:24,734 3191 | both accessibility as well as internationalization. 3192 | 3193 | 798 3194 | 00:38:24,803 --> 00:38:26,869 3195 | And that auditing your apps for accessibility, 3196 | 3197 | 799 3198 | 00:38:26,938 --> 00:38:29,572 3199 | which goes through how to take advantage of the accessibility 3200 | 3201 | 800 3202 | 00:38:29,641 --> 00:38:31,708 3203 | inspector to make your own apps accessible. 3204 | 3205 | 801 3206 | 00:38:34,545 --> 00:38:35,645 3207 | So, in closing, 3208 | 3209 | 802 3210 | 00:38:35,714 --> 00:38:39,081 3211 | I'd like to circle back to the why of it all. 3212 | 3213 | 803 3214 | 00:38:39,150 --> 00:38:42,885 3215 | Why should you take the time to do something like this? 3216 | 3217 | 804 3218 | 00:38:42,954 --> 00:38:46,055 3219 | Let me read you this quote from Sady, the woman who, 3220 | 3221 | 805 3222 | 00:38:46,124 --> 00:38:49,492 3223 | from the video that I showed at the beginning. 3224 | 3225 | 806 3226 | 00:38:49,561 --> 00:38:52,428 3227 | My love for technology is more than just a passion. 3228 | 3229 | 807 3230 | 00:38:52,497 --> 00:38:54,831 3231 | It gives me access to my world. 3232 | 3233 | 808 3234 | 00:38:54,899 --> 00:38:57,634 3235 | I need help in some areas, but assistive technology 3236 | 3237 | 809 3238 | 00:38:57,702 --> 00:39:00,069 3239 | enables me to communicate with my loved ones, and 3240 | 3241 | 810 3242 | 00:39:00,138 --> 00:39:01,771 3243 | pursue my career. 3244 | 3245 | 811 3246 | 00:39:01,840 --> 00:39:03,572 3247 | What everybody else can do with a keyboard and 3248 | 3249 | 812 3250 | 00:39:03,641 --> 00:39:06,009 3251 | a mouse, I can do with my two switches. 3252 | 3253 | 813 3254 | 00:39:08,012 --> 00:39:11,981 3255 | The common person's life is enhanced by technology. 3256 | 3257 | 814 3258 | 00:39:12,050 --> 00:39:15,318 3259 | But for somebody like Sady, technology is transformative. 3260 | 3261 | 815 3262 | 00:39:16,721 --> 00:39:19,455 3263 | It can make them independent in ways that really couldn't 3264 | 3265 | 816 3266 | 00:39:19,524 --> 00:39:22,024 3267 | have been imagined ten years ago. 3268 | 3269 | 817 3270 | 00:39:22,093 --> 00:39:24,894 3271 | It can allow them to do jobs that they couldn't have done, 3272 | 3273 | 818 3274 | 00:39:24,962 --> 00:39:27,163 3275 | have hobbies that they couldn't have had, or 3276 | 3277 | 819 3278 | 00:39:27,232 --> 00:39:29,866 3279 | experience the world in ways that they couldn't have. 3280 | 3281 | 820 3282 | 00:39:31,569 --> 00:39:34,303 3283 | So by taking the time and putting in the effort to make 3284 | 3285 | 821 3286 | 00:39:34,372 --> 00:39:37,039 3287 | sure that the work you do works for everyone, you can 3288 | 3289 | 822 3290 | 00:39:37,108 --> 00:39:41,110 3291 | have a lasting, meaningful impact on people's lives. 3292 | 3293 | 823 3294 | 00:39:41,178 --> 00:39:43,546 3295 | I promise you that you'll never find a more loyal 3296 | 3297 | 824 3298 | 00:39:43,614 --> 00:39:46,783 3299 | customer than the one whose life you made better. 3300 | 3301 | 825 3302 | 00:39:46,851 --> 00:39:48,485 3303 | Now I know that some of you may be thinking that that's 3304 | 3305 | 826 3306 | 00:39:48,553 --> 00:39:50,686 3307 | great and all, but there are some things that 3308 | 3309 | 827 3310 | 00:39:50,755 --> 00:39:53,956 3311 | really just don't work with accessibility. 3312 | 3313 | 828 3314 | 00:39:54,025 --> 00:39:55,692 3315 | And I challenge you to think different. 3316 | 3317 | 829 3318 | 00:39:57,261 --> 00:40:00,095 3319 | If you'd asked somebody 15 years ago how a blind person 3320 | 3321 | 830 3322 | 00:40:00,164 --> 00:40:00,930 3323 | could use a touch screen, 3324 | 3325 | 831 3326 | 00:40:00,999 --> 00:40:03,465 3327 | they probably would have told you that they couldn't. 3328 | 3329 | 832 3330 | 00:40:03,534 --> 00:40:04,534 3331 | And that's not true today. 3332 | 3333 | 833 3334 | 00:40:06,137 --> 00:40:09,906 3335 | Anything is really possible when you put your mind to it. 3336 | 3337 | 834 3338 | 00:40:09,975 --> 00:40:13,476 3339 | So this is a video of my colleague Ryan DJing at last 3340 | 3341 | 835 3342 | 00:40:13,545 --> 00:40:14,610 3343 | year's WWDC. 3344 | 3345 | 836 3346 | 00:40:14,679 --> 00:40:16,412 3347 | Where we awarded the app djay Pro for 3348 | 3349 | 837 3350 | 00:40:16,481 --> 00:40:19,148 3351 | their work on accessibility. 3352 | 3353 | 838 3354 | 00:40:19,217 --> 00:40:22,018 3355 | He DJed live on stage using the app with VoiceOver and 3356 | 3357 | 839 3358 | 00:40:22,087 --> 00:40:24,386 3359 | I'd like to play this brief clip for you now. 3360 | 3361 | 840 3362 | 00:40:24,455 --> 00:40:26,789 3363 | I have an iPad Pro running djay Pro and 3364 | 3365 | 841 3366 | 00:40:26,858 --> 00:40:28,324 3367 | this external mixer. 3368 | 3369 | 842 3370 | 00:40:28,392 --> 00:40:31,494 3371 | I'm gonna go ahead and throw on my headphones now. 3372 | 3373 | 843 3374 | 00:40:31,562 --> 00:40:35,598 3375 | So, here's the things that's interesting, the external DJ 3376 | 3377 | 844 3378 | 00:40:35,667 --> 00:40:39,001 3379 | controller acts as an extension of the onscreen UI. 3380 | 3381 | 845 3382 | 00:40:39,070 --> 00:40:42,104 3383 | And so I can use it to really create kind of a pretty 3384 | 3385 | 846 3386 | 00:40:42,173 --> 00:40:43,505 3387 | cool performance. 3388 | 3389 | 847 3390 | 00:40:43,574 --> 00:40:45,575 3391 | Also you're probably wondering what of this can I see. 3392 | 3393 | 848 3394 | 00:40:46,911 --> 00:40:47,743 3395 | Not much. 3396 | 3397 | 849 3398 | 00:40:47,812 --> 00:40:50,112 3399 | I pretty can see the light coming from the screen and 3400 | 3401 | 850 3402 | 00:40:50,181 --> 00:40:52,848 3403 | some of the colorful lights coming from the DJ controller, 3404 | 3405 | 851 3406 | 00:40:52,917 --> 00:40:54,417 3407 | but that's about it. 3408 | 3409 | 852 3410 | 00:40:54,485 --> 00:40:55,518 3411 | So knowing that, 3412 | 3413 | 853 3414 | 00:40:55,587 --> 00:40:58,154 3415 | we're gonna use VoiceOver to control this interface. 3416 | 3417 | 854 3418 | 00:40:58,223 --> 00:40:59,455 3419 | Let's go ahead and get the music started. 3420 | 3421 | 855 3422 | 00:40:59,524 --> 00:41:05,862 3423 | Play Okay, so the first thing I'd like to point out is that 3424 | 3425 | 856 3426 | 00:41:05,930 --> 00:41:08,598 3427 | I have access to information I've never had before. 3428 | 3429 | 857 3430 | 00:41:08,666 --> 00:41:10,132 3431 | For example, the time. 3432 | 3433 | 858 3434 | 00:41:10,201 --> 00:41:11,501 3435 | How much time is left on this track? 3436 | 3437 | 859 3438 | 00:41:11,569 --> 00:41:12,134 3439 | Let's find out. 3440 | 3441 | 860 3442 | 00:41:12,203 --> 00:41:14,804 3443 | Time deck one, 2:56. 3444 | 3445 | 861 3446 | 00:41:14,872 --> 00:41:15,471 3447 | That's perfect. 3448 | 3449 | 862 3450 | 00:41:15,540 --> 00:41:16,539 3451 | Okay, so I'm the DJ. 3452 | 3453 | 863 3454 | 00:41:16,608 --> 00:41:17,873 3455 | I'm supposed to keep the music going, and 3456 | 3457 | 864 3458 | 00:41:17,942 --> 00:41:19,642 3459 | now I have that kind of information. 3460 | 3461 | 865 3462 | 00:41:19,710 --> 00:41:21,710 3463 | I also can use these tempo sliders. 3464 | 3465 | 866 3466 | 00:41:21,779 --> 00:41:25,948 3467 | For example if I touch it right now, hold on a second. 3468 | 3469 | 867 3470 | 00:41:26,017 --> 00:41:27,817 3471 | tempo deck one 0% adjustable. 3472 | 3473 | 868 3474 | 00:41:27,885 --> 00:41:29,819 3475 | So we hear it's at its standard playback speed, but 3476 | 3477 | 869 3478 | 00:41:29,888 --> 00:41:34,623 3479 | if I slow it down using the DJ controller and 3480 | 3481 | 870 3482 | 00:41:34,692 --> 00:41:35,391 3483 | I touch it again. 3484 | 3485 | 871 3486 | 00:41:35,459 --> 00:41:38,227 3487 | Tempo deck one, -7.1%, adjustable. 3488 | 3489 | 872 3490 | 00:41:38,295 --> 00:41:40,996 3491 | We can hear exactly how far I've slown that track down, 3492 | 3493 | 873 3494 | 00:41:41,065 --> 00:41:42,131 3495 | that's really cool. 3496 | 3497 | 874 3498 | 00:41:42,199 --> 00:41:44,633 3499 | I also can use these multi-dimensional effect pads 3500 | 3501 | 875 3502 | 00:41:44,702 --> 00:41:47,570 3503 | for example if I touch it the first time Effects pad, 3504 | 3505 | 876 3506 | 00:41:47,638 --> 00:41:48,404 3507 | deck one. 3508 | 3509 | 877 3510 | 00:41:48,473 --> 00:41:54,576 3511 | But the second time. 3512 | 3513 | 878 3514 | 00:41:54,645 --> 00:41:55,744 3515 | Really cool stuff, right. 3516 | 3517 | 879 3518 | 00:41:55,813 --> 00:41:59,448 3519 | So between these effects pads, this external controller, and 3520 | 3521 | 880 3522 | 00:41:59,517 --> 00:42:00,916 3523 | all this accessible UI. 3524 | 3525 | 881 3526 | 00:42:00,985 --> 00:42:02,385 3527 | I can do a pretty cool performance. 3528 | 3529 | 882 3530 | 00:42:02,453 --> 00:42:05,187 3531 | So I'd like to go ahead and ask the stage crew to please 3532 | 3533 | 883 3534 | 00:42:05,256 --> 00:42:07,189 3535 | take VoiceOver off of the PA systems. 3536 | 3537 | 884 3538 | 00:42:07,258 --> 00:42:09,892 3539 | So that it's only available to me in my headphones. 3540 | 3541 | 885 3542 | 00:42:09,961 --> 00:42:11,293 3543 | Play deck one. 3544 | 3545 | 886 3546 | 00:42:11,362 --> 00:42:12,294 3547 | Cool, thank you very much. 3548 | 3549 | 887 3550 | 00:42:12,363 --> 00:42:13,996 3551 | And I'm gonna mix for you now. 3552 | 3553 | 888 3554 | 00:42:14,065 --> 00:42:14,496 3555 | Here we go. 3556 | 3557 | 889 3558 | 00:42:14,565 --> 00:42:24,607 3559 | [MUSIC] 3560 | 3561 | 890 3562 | 00:42:39,557 --> 00:42:44,960 3563 | [APPLAUSE] So because of the effort 3564 | 3565 | 891 3566 | 00:42:45,029 --> 00:42:48,030 3567 | that their team put in, people with vision impairments, 3568 | 3569 | 892 3570 | 00:42:48,099 --> 00:42:50,900 3571 | like Ryan, can DJ just as well as anybody else. 3572 | 3573 | 893 3574 | 00:42:50,969 --> 00:42:53,602 3575 | It's something that he loves to do, and it's only possible 3576 | 3577 | 894 3578 | 00:42:53,671 --> 00:42:56,339 3579 | because somebody at that company decided that it was 3580 | 3581 | 895 3582 | 00:42:56,407 --> 00:42:59,943 3583 | worth it. 3584 | 3585 | 896 3586 | 00:43:00,011 --> 00:43:01,143 3587 | Thank you. 3588 | 谢谢大家!(一片掌声雷鸣之中 3589 | 3590 | 897 3591 | 00:43:01,212 --> 00:43:04,413 3592 | [APPLAUSE] For more, 3593 | 本系列完美落幕。我们明年再见) 3594 | 3595 | 898 3596 | 00:43:04,482 --> 00:43:12,988 3597 | please visit us at stanford.edu 3598 | >> 更多课程详见 stanford.edu 3599 | -------------------------------------------------------------------------------- /tools/download.md: -------------------------------------------------------------------------------- 1 | [返回主页](../README.md) / [Back to Main Page](../en/README.md) 2 | 3 | # Videos 4 | 5 | 1. [Introduction to iOS 10, Xcode 8 and Swift 3](https://itunesu-assets.itunes.apple.com/apple-assets-us-std-000001/CobaltPublic122/v4/02/4d/e9/024de95b-8afc-89b9-e4ba-ef8e08d5d13f/324-3685031694599443811-01_1_09_17_720p3000_for_iTunes.m4v) -- [中英字幕](https://raw.githubusercontent.com/ApolloZhu/Developing-iOS-10-Apps-with-Swift/master/subtitles/1.%20Introduction%20to%20iOS%2010%2C%20Xcode%208%20and%20Swift%203.srt) 6 | 2. [MVC; iOS, Xcode and Swift Demonstration](https://itunesu-assets.itunes.apple.com/apple-assets-us-std-000001/CobaltPublic122/v4/a5/2c/bd/a52cbd78-81b0-21f4-ddd2-938d88736d1e/305-7182155496523276740-02_1_11_17_720p3000cc.m4v) -- [中英字幕](https://raw.githubusercontent.com/ApolloZhu/Developing-iOS-10-Apps-with-Swift/master/subtitles/2.%20MVC%3B%20iOS%2C%20Xcode%20and%20Swift%20Demonstration.srt) 7 | 3. [More Swift and the Foundation Framework](https://itunesu-assets.itunes.apple.com/apple-assets-us-std-000001/CobaltPublic122/v4/06/89/5f/06895f76-941c-d5c7-c67a-b4a344ff04da/319-4626069356218693519-03_1_18_17_720p3mb_iTunes.m4v) -- [中英字幕](https://raw.githubusercontent.com/ApolloZhu/Developing-iOS-10-Apps-with-Swift/master/subtitles/3.%20More%20Swift%20and%20the%20Foundation%20Framework.srt) 8 | 4. [Views](https://itunesu-assets.itunes.apple.com/apple-assets-us-std-000001/CobaltPublic82/v4/2f/19/00/2f190095-0ad9-68da-de79-460b9b9a5b21/307-1808792045946883749-04_1_23_17_1080p_1_720p_3000.m4v) 9 | 5. [Gestures and Multiple MVCs](https://itunesu-assets.itunes.apple.com/apple-assets-us-std-000001/CobaltPublic122/v4/3b/e8/87/3be887ea-089b-2532-81eb-3f21a789d79c/328-6978379452515215276-05_1_25_17_720p_3000cc.m4v) 10 | 6. [Multiple MVCs, View Controller Lifecycle, and Memory Management](https://itunesu-assets.itunes.apple.com/apple-assets-us-std-000001/CobaltPublic111/v4/ac/9b/8d/ac9b8d88-ca19-3364-c91c-ad88b76457aa/330-554709660381912333-06_1_30_17_720p_3mbcc.m4v) 11 | 7. [Error Handling, Extensions, Protocols, Delegation, and Scroll View](https://itunesu-assets.itunes.apple.com/apple-assets-us-std-000001/CobaltPublic122/v4/f4/06/12/f406127e-4cdc-3859-555e-49872b7736d0/322-5522795565946608562-07_CS193p_2_01_17_720p_3000cc.m4v) 12 | 8. [Multithreading and Text Field](https://itunesu-assets.itunes.apple.com/apple-assets-us-std-000001/CobaltPublic122/v4/b7/64/30/b76430e9-a1e1-d257-c14f-b822dd314749/327-2534985180508386683-08_CS193p_2_06_17_720p_3000cc.m4v) 13 | 9. [Table View](https://itunesu-assets.itunes.apple.com/apple-assets-us-std-000001/CobaltPublic111/v4/2f/e0/34/2fe0348d-de16-64ba-0f94-0d0dac7121bb/337-8752859105577614662-09_CS193p_2_08_17_720p_3000.m4v) 14 | 10. [Core Data](https://itunesu-assets.itunes.apple.com/apple-assets-us-std-000001/CobaltPublic122/v4/84/a3/a3/84a3a3aa-b6f5-fe7c-2cc5-9719f2049046/311-2755894078252338227-10_CS193p_2_13_17_revised_WIP03_720p3000.mp4) 15 | 11. [Core Data Demo](https://itunesu-assets.itunes.apple.com/apple-assets-us-std-000001/CobaltPublic122/v4/dd/07/87/dd07870d-cb3c-c2fb-63b0-e8453452b0b2/318-3789265740124671308-11_CS193p_2_15_17_720p_3000.m4v) 16 | 12. [Autolayout](https://itunesu-assets.itunes.apple.com/apple-assets-us-std-000001/CobaltPublic122/v4/c7/05/85/c7058540-443c-4cc8-74fc-ef151c6e25f3/316-5259175746296795476-12_CS193p_2_22_17_revised_WIP03_720p3000cc.mp4) 17 | 13. [Timer and Animation](https://itunesu-assets.itunes.apple.com/apple-assets-us-std-000001/CobaltPublic122/v4/dd/9e/bc/dd9ebcdd-f8ad-05bd-e0a1-bc8163f78125/303-6364109281891900174-13_CS193p_2_27_17_720p3000cc.mp4) 18 | 14. [Dynamic Animation Demo](https://itunesu-assets.itunes.apple.com/apple-assets-us-std-000001/CobaltPublic111/v4/69/e6/a6/69e6a628-a269-e496-b51d-afaf25c114a1/323-8980879071774720493-14_3_01_17_720p3000cc.mp4) 19 | 15. [More Segues](https://itunesu-assets.itunes.apple.com/apple-assets-us-std-000001/CobaltPublic111/v4/6b/5f/54/6b5f5450-12d7-23cf-a3ec-2d313923875c/324-2875002592034152487-15_3_06_17_wip2_1080p_720p_3mb_cc.m4v) 20 | 16. [Alerts and Action Sheets, Notifications, Application Lifecycle, and Persistence](https://itunesu-assets.itunes.apple.com/apple-assets-us-std-000001/CobaltPublic127/v4/69/f6/c7/69f6c757-e44e-245e-f539-bee224372dc6/323-7683342428341604999-16_3_08_17_720p_3000cc.m4v) 21 | 17. [Accessibility](https://itunesu-assets.itunes.apple.com/apple-assets-us-std-000001/CobaltPublic127/v4/7d/68/df/7d68dfc6-f9eb-c72c-905c-7557d96a8b5b/330-3844304494261784016-17_3_13_17_2_720p_3000cc.m4v) 22 | 23 | # Slides 24 | 25 | 1. [Lecture 1 Slides](https://itunesu-assets.itunes.apple.com/apple-assets-us-std-000001/CobaltPublic122/v4/4a/a7/26/4aa726ce-b9a0-dfc6-1caa-a0c21279f4fe/302-9205777791325237790-CS193P_Winter_17_Lecture_1.pdf) 26 | 2. [Lecture 2 Slides](https://itunesu-assets.itunes.apple.com/apple-assets-us-std-000001/CobaltPublic111/v4/3e/4f/87/3e4f87b1-ff63-7239-d64a-42a11e95a071/309-1216292682689348645-CS193P_Winter_17_Lecture_2.pdf) 27 | 3. [Lecture 3 Slides](https://itunesu-assets.itunes.apple.com/apple-assets-us-std-000001/CobaltPublic122/v4/3b/26/03/3b260345-a591-ad12-a089-cb5bd3e6fcc5/306-3349052672936698336-CS193P_Winter_17_Lecture_3.pdf) 28 | 4. [Lecture 4 Slides](https://itunesu-assets.itunes.apple.com/apple-assets-us-std-000001/CobaltPublic122/v4/85/52/eb/8552ebd3-2577-33c2-5e15-ea0b21109d10/322-2432714053453526314-CS193P_Winter_17_Lecture_4.pdf) 29 | 5. [Lecture 5 Slides](https://itunesu-assets.itunes.apple.com/apple-assets-us-std-000001/CobaltPublic122/v4/10/9d/2b/109d2b59-c432-5e4e-63a4-40ccdd9f04dd/321-3063962921399737466-CS193P_Winter_17_Lecture_5.pdf) 30 | 6. [Lecture 6 Slides](https://itunesu-assets.itunes.apple.com/apple-assets-us-std-000001/CobaltPublic111/v4/03/b9/48/03b94846-86b1-78b4-4f0d-db883b178a93/329-5253968848372497904-CS193P_Winter_17_Lecture_6.pdf) 31 | 7. [Lecture 7 Slides](https://itunesu-assets.itunes.apple.com/apple-assets-us-std-000001/CobaltPublic122/v4/65/32/4e/65324e1a-3d03-cf1b-2bf8-ea23c42f450b/332-5273798659857609329-CS193P_Winter_17_Lecture_7.pdf) 32 | 8. [Lecture 8 Slides](https://itunesu-assets.itunes.apple.com/apple-assets-us-std-000001/CobaltPublic122/v4/c6/24/c4/c624c4a1-781c-613f-f86f-19c9b013d92a/329-5089714432926654345-CS193P_Winter_17_Lecture_8.pdf) 33 | 9. [Lecture 9 Slides](https://itunesu-assets.itunes.apple.com/apple-assets-us-std-000001/CobaltPublic122/v4/8f/44/45/8f4445c4-76f3-f356-69c3-3cd384470cc9/314-2744839717659725615-CS193P_Winter_17_Lecture_9.pdf) 34 | 10. [Lecture 10 Slides](https://itunesu-assets.itunes.apple.com/apple-assets-us-std-000001/CobaltPublic122/v4/6f/54/7c/6f547cc8-1d90-88c8-77e4-d9ad9a413144/312-2771300030580731448-CS193P_Winter_17_Lecture_10.pdf) 35 | 12. [Lecture 12 Slides](https://itunesu-assets.itunes.apple.com/apple-assets-us-std-000001/CobaltPublic122/v4/38/ff/0d/38ff0d79-0ba4-94a2-2d3f-2e3e62ffbe23/309-3567392098680090005-CS193P_Winter_17_Lecture_12.pdf) 36 | 13. [Lecture 13 Slides](https://itunesu-assets.itunes.apple.com/apple-assets-us-std-000001/CobaltPublic122/v4/8e/4e/5d/8e4e5d8d-d403-28ff-3a2b-47fe17a00460/306-2848100867125253549-CS193P_Winter_17_Lecture_13.pdf) 37 | 15. [Lecture 15 Slides](https://itunesu-assets.itunes.apple.com/apple-assets-us-std-000001/CobaltPublic111/v4/82/e7/17/82e71729-4a3b-f604-4d4b-588a0b64c5de/311-6653904285911219618-CS193P_Winter_17_Lecture_15.pdf) 38 | 16. [Lecture 16 Slides](https://itunesu-assets.itunes.apple.com/apple-assets-us-std-000001/CobaltPublic127/v4/d2/5f/36/d25f36ef-e1d7-c296-9094-621db387fea6/319-5542480879425152826-CS193P_Winter_17_Lecture_16.pdf) 39 | 40 | # Demo Code 41 | 42 | 4. [Lecture 4 Demo Code: FaceIt](https://itunesu-assets.itunes.apple.com/apple-assets-us-std-000001/CobaltPublic111/v4/90/23/06/90230676-8974-d6f4-8730-db23b02981d4/328-3425656378839915562-L4_Demo_Code.pdf) 43 | 5. [Lecture 5 Demo Code: FaceIt](https://itunesu-assets.itunes.apple.com/apple-assets-us-std-000001/CobaltPublic122/v4/6e/d9/4d/6ed94dd8-30e3-f0d4-0646-0b55d300a3d2/329-3399216634488067626-L5_Demo_Code.pdf) 44 | 6. [Lecture 6 Demo Code: FaceIt](https://itunesu-assets.itunes.apple.com/apple-assets-us-std-000001/CobaltPublic111/v4/51/0b/bc/510bbce2-ec82-b3ab-de8f-508ba60f1be9/334-3428961349568251114-L6_Demo_Code.pdf) 45 | 7. [Lecture 7 Demo Code: Cassini](https://itunesu-assets.itunes.apple.com/apple-assets-us-std-000001/CobaltPublic122/v4/7a/ac/a7/7aaca767-9f2c-8bc3-a32a-3c3456660545/330-3352947087241042856-L7_Demo_Code.pdf) 46 | 8. [Lecture 8 Demo Code: Cassini](https://itunesu-assets.itunes.apple.com/apple-assets-us-std-000001/CobaltPublic111/v4/c2/4b/5e/c24b5e00-66b3-6e1c-c024-a98b21736424/311-1937232581840857786-L8_Demo_Code.pdf) 47 | 9. [Lecture 9 Demo Code: Smashtag](https://itunesu-assets.itunes.apple.com/apple-assets-us-std-000001/CobaltPublic111/v4/bd/b9/a3/bdb9a33e-0801-7cd3-5afc-849b44b0fa6d/316-1975239713004461915-L9_Demo_Code.pdf) 48 | 11. [Lecture 11 Demo Code: Smashtag](https://itunesu-assets.itunes.apple.com/apple-assets-us-std-000001/CobaltPublic111/v4/11/da/76/11da7624-122d-4667-3b7c-84077311be31/308-6440268317978219720-L11_Demo_Code.pdf) 49 | 13. [Lecture 13 Demo Code: FaceIt](https://itunesu-assets.itunes.apple.com/apple-assets-us-std-000001/CobaltPublic111/v4/88/4e/7c/884e7c7c-f164-5262-6884-4734c0101c0f/302-4707211118905059285-L13_Demo_Code.pdf) 50 | 14. [Lecture 14 Demo Code: Asteroids](https://itunesu-assets.itunes.apple.com/apple-assets-us-std-000001/CobaltPublic122/v4/b0/d3/53/b0d35343-960c-c35c-cc51-6a6161d2fe56/308-3158927732347667387-L14_Demo_Code.pdf) 51 | 15. [Lecture 15 Demo Code: FaceIt Segues](https://itunesu-assets.itunes.apple.com/apple-assets-us-std-000001/CobaltPublic111/v4/b0/64/55/b064559c-073f-a6b5-38a8-bc1b1519332f/307-7933058492198201398-L15_Demo_Code.pdf) 52 | 53 | # Reading Assignments 54 | 55 | 1. [Intro to Swift](https://itunesu-assets.itunes.apple.com/apple-assets-us-std-000001/CobaltPublic111/v4/7d/38/f2/7d38f27a-3b82-de90-2f2c-2ee40514a56b/329-3366122602226359241-CS193P_Winter_17_Reading_1.pdf) 56 | 2. [More Swift](https://itunesu-assets.itunes.apple.com/apple-assets-us-std-000001/CobaltPublic122/v4/50/27/6e/50276e9c-bb5a-2cd8-db64-c027a1f98507/318-8166708043639963524-CS193P_Winter_17_Reading_2.pdf) 57 | 3. [The Rest of Swift](https://itunesu-assets.itunes.apple.com/apple-assets-us-std-000001/CobaltPublic111/v4/81/cf/fd/81cffd52-6c5a-1ecb-7366-875a41ad6465/303-5453919407785590613-CS193P_Winter_17_Reading_3.pdf) 58 | 59 | # Programming Projects 60 | 61 | 1. [Calculator](https://itunesu-assets.itunes.apple.com/apple-assets-us-std-000001/CobaltPublic122/v4/3b/5f/8a/3b5f8a08-0535-8a32-0854-8a1d4c44fc83/330-8784676202759607348-CS193P_Winter_17_Project_1.pdf) 62 | 2. [Calculator Brain](https://itunesu-assets.itunes.apple.com/apple-assets-us-std-000001/CobaltPublic122/v4/16/6d/ee/166dee9f-1ff6-f7d1-99cc-2590c616c7b3/334-4539701711666409931-CS193P_Winter_17_Assignment_2_v4.pdf) 63 | 3. [Graphing Calculator](https://itunesu-assets.itunes.apple.com/apple-assets-us-std-000001/CobaltPublic111/v4/d3/cc/f8/d3ccf813-811f-0a8a-0f97-a3da51065fcb/305-7790343223312375480-CS193P_Winter_17_Assignment_3.pdf) 64 | 4. [Smashtag Mentions](https://itunesu-assets.itunes.apple.com/apple-assets-us-std-000001/CobaltPublic111/v4/9f/f5/20/9ff520eb-7558-aa61-121f-841efed2460f/327-6109771528612506048-CS193P_Winter_17_Assignment_4.pdf) 65 | 5. [Smashtag Mention Popularity](https://itunesu-assets.itunes.apple.com/apple-assets-us-std-000001/CobaltPublic111/v4/53/93/f1/5393f1c8-8612-b547-1cc7-06ba86fa486a/322-1087034889572316150-CS193P_Winter_17_Assignment_5.pdf) 66 | 67 | -------------------------------------------------------------------------------- /tools/main.swift: -------------------------------------------------------------------------------- 1 | // 2 | // Created by Apollo Zhu on 3/12/17. 3 | // This work is licensed under a 4 | // Creative Commons Attribution-NonCommercial-ShareAlike 3.0 United States License. 5 | // 6 | 7 | import Foundation 8 | import AppKit 9 | 10 | /// itunes.apple.com/us/course/developing-ios-10-apps-with-swift/id<#iTunesUCourseID#> 11 | let iTunesUCourseID = 1198467120 12 | 13 | enum ResourceType: String, CustomStringConvertible { 14 | // Raw Type: video/x-m4v, video/mp4 15 | case video = "Video" 16 | // Raw Type: application/pdf 17 | case slides = "Slides" 18 | case demoCode = "Demo Code" 19 | case readingAssignment = "Reading Assignment" 20 | case programmingProject = "Programming Project" 21 | 22 | static let all: LazyCollection<[ResourceType]> = [.video, .slides, .demoCode, .readingAssignment, .programmingProject].lazy 23 | 24 | var description: String { 25 | switch self { 26 | case .slides, .demoCode: 27 | return rawValue 28 | default: 29 | return "\(rawValue)s" 30 | } 31 | } 32 | } 33 | 34 | struct Resource: CustomStringConvertible { 35 | let index: Int 36 | let title: String 37 | let type: ResourceType 38 | let url: String 39 | 40 | init(title: String, rawType: String, url: String) { 41 | self.url = url 42 | 43 | if rawType.contains("video") { 44 | type = .video 45 | } else if let resType = ResourceType.all.first(where: { title.contains($0.rawValue) }) { 46 | type = resType 47 | } else { 48 | fatalError("Unknown Raw Type \(rawType)") 49 | } 50 | 51 | var parts: [String] 52 | if type == .video { 53 | // 4. Views -> index: 4, title: Views 54 | parts = title.components(separatedBy: ". ") 55 | } else if [.readingAssignment, .programmingProject].contains(type) { 56 | // Reading Assignment 2: More Swift -> index: 2, title: More Swift 57 | // Programming Project 2: Calculator Brain -> index: 2, title: Calculator Brain 58 | parts = title.components(separatedBy: ": ") 59 | parts[0] = parts[0].components(separatedBy: " ")[2] 60 | } else { 61 | // Lecture 6 Slides -> index: 6, title: Lecture 6 Slides 62 | // Lecture 9 Demo Code: Smashtag -> index: 9, title: Lecture 9 Demo Code: Smashtag 63 | parts = [title.components(separatedBy: " ")[1], title] 64 | } 65 | self.index = Int(parts[0])! 66 | self.title = parts[1] 67 | } 68 | 69 | var description: String { 70 | return "\(index). [\(title)](\(url))" 71 | } 72 | } 73 | 74 | /* Example XML 75 | 76 | 77 | Paul Hegarty 78 | 79 | 80 | <![CDATA[ <#Title#> ]]> 81 | 82 | 1/COETAIHAJLZIQXJI/MAEC2FBSEERRMTUH 83 | 2017-03-10T18:19:21PST 84 | 2017-03-10T17:36:29PST 85 | 86 | 87 | 88 | 89 | 90 | */ 91 | class ParsingDelegate: NSObject, XMLParserDelegate { 92 | var resources = [Resource]() 93 | 94 | var title: String? 95 | var isParsingTitle = false 96 | var type: String? 97 | var url: String? 98 | 99 | func parser(_ parser: XMLParser, didStartElement elementName: String, namespaceURI: String?, qualifiedName qName: String?, attributes attributeDict: [String : String] = [:]) { 100 | if elementName == "link" { 101 | type = attributeDict["type"] 102 | url = attributeDict["href"] 103 | } else if elementName == "title" { 104 | isParsingTitle = true 105 | } 106 | } 107 | 108 | func parser(_ parser: XMLParser, foundCDATA CDATABlock: Data) { 109 | if isParsingTitle { 110 | if let title = String(data: CDATABlock, encoding: .utf8) { 111 | self.title = title 112 | } else { 113 | fatalError("Unable to parse title from \(CDATABlock)") 114 | } 115 | } 116 | } 117 | 118 | func parser(_ parser: XMLParser, didEndElement elementName: String, namespaceURI: String?, qualifiedName qName: String?) { 119 | if elementName == "title" { 120 | isParsingTitle = false 121 | } else if elementName == "entry" { 122 | resources.append(Resource(title: title!, rawType: type!, url: url!)) 123 | title = nil 124 | isParsingTitle = false 125 | type = nil 126 | url = nil 127 | } 128 | } 129 | 130 | func parserDidEndDocument(_ parser: XMLParser) { 131 | let sorted = ResourceType.all.map { [resources] type in 132 | resources.filter { $0.type == type } .sorted { $0.index < $1.index } 133 | } 134 | 135 | var out = "[返回主页](../README.md) / [Back to Main Page](../en/README.md)\n\n" 136 | for (index, type) in ResourceType.all.enumerated() { 137 | guard !sorted[index].isEmpty else { fatalError("Missing Resources of Type \(type)") } 138 | out += "# \(type)\n\n" 139 | + "\(sorted[index].reduce("") { "\($0)\($1)\n" })\n" 140 | } 141 | 142 | let cwd = CommandLine.arguments.first { $0.contains(#file) } ?? FileManager.default.currentDirectoryPath 143 | let url = URL(fileURLWithPath: cwd).deletingLastPathComponent().appendingPathComponent("download.md") 144 | do { 145 | try out.write(to: url, atomically: true, encoding: .utf8) 146 | #if swift(>=4) 147 | let workspace = NSWorkspace.shared 148 | #else 149 | let workspace = NSWorkspace.shared() 150 | #endif 151 | workspace.activateFileViewerSelecting([url]) 152 | } catch { 153 | print(out) 154 | } 155 | } 156 | } 157 | 158 | let url = URL(string: "https://itunesu.itunes.apple.com/WebObjects/LZDirectory.woa/ra/directory/courses/\(iTunesUCourseID)/feed")! 159 | let parser = XMLParser(contentsOf: url)! 160 | let delegate = ParsingDelegate() 161 | parser.delegate = delegate 162 | parser.parse() 163 | -------------------------------------------------------------------------------- /tools/update.xcodeproj/project.pbxproj: -------------------------------------------------------------------------------- 1 | // !$*UTF8*$! 2 | { 3 | archiveVersion = 1; 4 | classes = { 5 | }; 6 | objectVersion = 46; 7 | objects = { 8 | 9 | /* Begin PBXBuildFile section */ 10 | 52D71C3B1E7612ED0036EE1C /* main.swift in Sources */ = {isa = PBXBuildFile; fileRef = 52D71C3A1E7612ED0036EE1C /* main.swift */; }; 11 | /* End PBXBuildFile section */ 12 | 13 | /* Begin PBXCopyFilesBuildPhase section */ 14 | 522CA2021E75E64B00EE2E2F /* CopyFiles */ = { 15 | isa = PBXCopyFilesBuildPhase; 16 | buildActionMask = 2147483647; 17 | dstPath = /usr/share/man/man1/; 18 | dstSubfolderSpec = 0; 19 | files = ( 20 | ); 21 | runOnlyForDeploymentPostprocessing = 1; 22 | }; 23 | /* End PBXCopyFilesBuildPhase section */ 24 | 25 | /* Begin PBXFileReference section */ 26 | 522CA2041E75E64B00EE2E2F /* update */ = {isa = PBXFileReference; explicitFileType = "compiled.mach-o.executable"; includeInIndex = 0; path = update; sourceTree = BUILT_PRODUCTS_DIR; }; 27 | 52D71C3A1E7612ED0036EE1C /* main.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = main.swift; sourceTree = SOURCE_ROOT; }; 28 | /* End PBXFileReference section */ 29 | 30 | /* Begin PBXFrameworksBuildPhase section */ 31 | 522CA2011E75E64B00EE2E2F /* Frameworks */ = { 32 | isa = PBXFrameworksBuildPhase; 33 | buildActionMask = 2147483647; 34 | files = ( 35 | ); 36 | runOnlyForDeploymentPostprocessing = 0; 37 | }; 38 | /* End PBXFrameworksBuildPhase section */ 39 | 40 | /* Begin PBXGroup section */ 41 | 522CA1FB1E75E64B00EE2E2F = { 42 | isa = PBXGroup; 43 | children = ( 44 | 52D71C3A1E7612ED0036EE1C /* main.swift */, 45 | 522CA2051E75E64B00EE2E2F /* Products */, 46 | ); 47 | sourceTree = ""; 48 | }; 49 | 522CA2051E75E64B00EE2E2F /* Products */ = { 50 | isa = PBXGroup; 51 | children = ( 52 | 522CA2041E75E64B00EE2E2F /* update */, 53 | ); 54 | name = Products; 55 | sourceTree = ""; 56 | }; 57 | /* End PBXGroup section */ 58 | 59 | /* Begin PBXNativeTarget section */ 60 | 522CA2031E75E64B00EE2E2F /* update */ = { 61 | isa = PBXNativeTarget; 62 | buildConfigurationList = 522CA20B1E75E64B00EE2E2F /* Build configuration list for PBXNativeTarget "update" */; 63 | buildPhases = ( 64 | 522CA2001E75E64B00EE2E2F /* Sources */, 65 | 522CA2011E75E64B00EE2E2F /* Frameworks */, 66 | 522CA2021E75E64B00EE2E2F /* CopyFiles */, 67 | ); 68 | buildRules = ( 69 | ); 70 | dependencies = ( 71 | ); 72 | name = update; 73 | productName = update; 74 | productReference = 522CA2041E75E64B00EE2E2F /* update */; 75 | productType = "com.apple.product-type.tool"; 76 | }; 77 | /* End PBXNativeTarget section */ 78 | 79 | /* Begin PBXProject section */ 80 | 522CA1FC1E75E64B00EE2E2F /* Project object */ = { 81 | isa = PBXProject; 82 | attributes = { 83 | LastSwiftUpdateCheck = 0820; 84 | LastUpgradeCheck = 0900; 85 | ORGANIZATIONNAME = WWITDC; 86 | TargetAttributes = { 87 | 522CA2031E75E64B00EE2E2F = { 88 | CreatedOnToolsVersion = 8.2.1; 89 | DevelopmentTeam = 2H866F22W7; 90 | LastSwiftMigration = 0900; 91 | ProvisioningStyle = Automatic; 92 | }; 93 | }; 94 | }; 95 | buildConfigurationList = 522CA1FF1E75E64B00EE2E2F /* Build configuration list for PBXProject "update" */; 96 | compatibilityVersion = "Xcode 3.2"; 97 | developmentRegion = English; 98 | hasScannedForEncodings = 0; 99 | knownRegions = ( 100 | en, 101 | ); 102 | mainGroup = 522CA1FB1E75E64B00EE2E2F; 103 | productRefGroup = 522CA2051E75E64B00EE2E2F /* Products */; 104 | projectDirPath = ""; 105 | projectRoot = ""; 106 | targets = ( 107 | 522CA2031E75E64B00EE2E2F /* update */, 108 | ); 109 | }; 110 | /* End PBXProject section */ 111 | 112 | /* Begin PBXSourcesBuildPhase section */ 113 | 522CA2001E75E64B00EE2E2F /* Sources */ = { 114 | isa = PBXSourcesBuildPhase; 115 | buildActionMask = 2147483647; 116 | files = ( 117 | 52D71C3B1E7612ED0036EE1C /* main.swift in Sources */, 118 | ); 119 | runOnlyForDeploymentPostprocessing = 0; 120 | }; 121 | /* End PBXSourcesBuildPhase section */ 122 | 123 | /* Begin XCBuildConfiguration section */ 124 | 522CA2091E75E64B00EE2E2F /* Debug */ = { 125 | isa = XCBuildConfiguration; 126 | buildSettings = { 127 | ALWAYS_SEARCH_USER_PATHS = NO; 128 | CLANG_ANALYZER_NONNULL = YES; 129 | CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x"; 130 | CLANG_CXX_LIBRARY = "libc++"; 131 | CLANG_ENABLE_MODULES = YES; 132 | CLANG_ENABLE_OBJC_ARC = YES; 133 | CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES; 134 | CLANG_WARN_BOOL_CONVERSION = YES; 135 | CLANG_WARN_COMMA = YES; 136 | CLANG_WARN_CONSTANT_CONVERSION = YES; 137 | CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; 138 | CLANG_WARN_DOCUMENTATION_COMMENTS = YES; 139 | CLANG_WARN_EMPTY_BODY = YES; 140 | CLANG_WARN_ENUM_CONVERSION = YES; 141 | CLANG_WARN_INFINITE_RECURSION = YES; 142 | CLANG_WARN_INT_CONVERSION = YES; 143 | CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES; 144 | CLANG_WARN_OBJC_LITERAL_CONVERSION = YES; 145 | CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; 146 | CLANG_WARN_RANGE_LOOP_ANALYSIS = YES; 147 | CLANG_WARN_STRICT_PROTOTYPES = YES; 148 | CLANG_WARN_SUSPICIOUS_MOVE = YES; 149 | CLANG_WARN_UNREACHABLE_CODE = YES; 150 | CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; 151 | CODE_SIGN_IDENTITY = "-"; 152 | COPY_PHASE_STRIP = NO; 153 | DEBUG_INFORMATION_FORMAT = dwarf; 154 | ENABLE_STRICT_OBJC_MSGSEND = YES; 155 | ENABLE_TESTABILITY = YES; 156 | GCC_C_LANGUAGE_STANDARD = gnu99; 157 | GCC_DYNAMIC_NO_PIC = NO; 158 | GCC_NO_COMMON_BLOCKS = YES; 159 | GCC_OPTIMIZATION_LEVEL = 0; 160 | GCC_PREPROCESSOR_DEFINITIONS = ( 161 | "DEBUG=1", 162 | "$(inherited)", 163 | ); 164 | GCC_WARN_64_TO_32_BIT_CONVERSION = YES; 165 | GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; 166 | GCC_WARN_UNDECLARED_SELECTOR = YES; 167 | GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; 168 | GCC_WARN_UNUSED_FUNCTION = YES; 169 | GCC_WARN_UNUSED_VARIABLE = YES; 170 | MACOSX_DEPLOYMENT_TARGET = 10.9; 171 | MTL_ENABLE_DEBUG_INFO = YES; 172 | ONLY_ACTIVE_ARCH = YES; 173 | SDKROOT = macosx; 174 | SWIFT_OPTIMIZATION_LEVEL = "-Onone"; 175 | }; 176 | name = Debug; 177 | }; 178 | 522CA20A1E75E64B00EE2E2F /* Release */ = { 179 | isa = XCBuildConfiguration; 180 | buildSettings = { 181 | ALWAYS_SEARCH_USER_PATHS = NO; 182 | CLANG_ANALYZER_NONNULL = YES; 183 | CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x"; 184 | CLANG_CXX_LIBRARY = "libc++"; 185 | CLANG_ENABLE_MODULES = YES; 186 | CLANG_ENABLE_OBJC_ARC = YES; 187 | CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES; 188 | CLANG_WARN_BOOL_CONVERSION = YES; 189 | CLANG_WARN_COMMA = YES; 190 | CLANG_WARN_CONSTANT_CONVERSION = YES; 191 | CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; 192 | CLANG_WARN_DOCUMENTATION_COMMENTS = YES; 193 | CLANG_WARN_EMPTY_BODY = YES; 194 | CLANG_WARN_ENUM_CONVERSION = YES; 195 | CLANG_WARN_INFINITE_RECURSION = YES; 196 | CLANG_WARN_INT_CONVERSION = YES; 197 | CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES; 198 | CLANG_WARN_OBJC_LITERAL_CONVERSION = YES; 199 | CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; 200 | CLANG_WARN_RANGE_LOOP_ANALYSIS = YES; 201 | CLANG_WARN_STRICT_PROTOTYPES = YES; 202 | CLANG_WARN_SUSPICIOUS_MOVE = YES; 203 | CLANG_WARN_UNREACHABLE_CODE = YES; 204 | CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; 205 | CODE_SIGN_IDENTITY = "-"; 206 | COPY_PHASE_STRIP = NO; 207 | DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym"; 208 | ENABLE_NS_ASSERTIONS = NO; 209 | ENABLE_STRICT_OBJC_MSGSEND = YES; 210 | GCC_C_LANGUAGE_STANDARD = gnu99; 211 | GCC_NO_COMMON_BLOCKS = YES; 212 | GCC_WARN_64_TO_32_BIT_CONVERSION = YES; 213 | GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; 214 | GCC_WARN_UNDECLARED_SELECTOR = YES; 215 | GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; 216 | GCC_WARN_UNUSED_FUNCTION = YES; 217 | GCC_WARN_UNUSED_VARIABLE = YES; 218 | MACOSX_DEPLOYMENT_TARGET = 10.9; 219 | MTL_ENABLE_DEBUG_INFO = NO; 220 | SDKROOT = macosx; 221 | SWIFT_OPTIMIZATION_LEVEL = "-Owholemodule"; 222 | }; 223 | name = Release; 224 | }; 225 | 522CA20C1E75E64B00EE2E2F /* Debug */ = { 226 | isa = XCBuildConfiguration; 227 | buildSettings = { 228 | CONFIGURATION_BUILD_DIR = "$(PROJECT_DIR)/$(CONFIGURATION)"; 229 | DEVELOPMENT_TEAM = 2H866F22W7; 230 | PRODUCT_NAME = "$(TARGET_NAME)"; 231 | SWIFT_SWIFT3_OBJC_INFERENCE = Default; 232 | SWIFT_VERSION = 4.0; 233 | }; 234 | name = Debug; 235 | }; 236 | 522CA20D1E75E64B00EE2E2F /* Release */ = { 237 | isa = XCBuildConfiguration; 238 | buildSettings = { 239 | CONFIGURATION_BUILD_DIR = "$(PROJECT_DIR)/$(CONFIGURATION)"; 240 | DEVELOPMENT_TEAM = 2H866F22W7; 241 | PRODUCT_NAME = "$(TARGET_NAME)"; 242 | SWIFT_SWIFT3_OBJC_INFERENCE = Default; 243 | SWIFT_VERSION = 4.0; 244 | }; 245 | name = Release; 246 | }; 247 | /* End XCBuildConfiguration section */ 248 | 249 | /* Begin XCConfigurationList section */ 250 | 522CA1FF1E75E64B00EE2E2F /* Build configuration list for PBXProject "update" */ = { 251 | isa = XCConfigurationList; 252 | buildConfigurations = ( 253 | 522CA2091E75E64B00EE2E2F /* Debug */, 254 | 522CA20A1E75E64B00EE2E2F /* Release */, 255 | ); 256 | defaultConfigurationIsVisible = 0; 257 | defaultConfigurationName = Release; 258 | }; 259 | 522CA20B1E75E64B00EE2E2F /* Build configuration list for PBXNativeTarget "update" */ = { 260 | isa = XCConfigurationList; 261 | buildConfigurations = ( 262 | 522CA20C1E75E64B00EE2E2F /* Debug */, 263 | 522CA20D1E75E64B00EE2E2F /* Release */, 264 | ); 265 | defaultConfigurationIsVisible = 0; 266 | defaultConfigurationName = Release; 267 | }; 268 | /* End XCConfigurationList section */ 269 | }; 270 | rootObject = 522CA1FC1E75E64B00EE2E2F /* Project object */; 271 | } 272 | -------------------------------------------------------------------------------- /tools/update.xcodeproj/project.xcworkspace/contents.xcworkspacedata: -------------------------------------------------------------------------------- 1 | 2 | 4 | 6 | 7 | 8 | -------------------------------------------------------------------------------- /translation-style-guide.md: -------------------------------------------------------------------------------- 1 | # 翻译标准/校对规则 2 | 3 | ## 基本要求 4 | 5 | 1. 请大家翻译和校对的时候尽量对照原视频,结合上下文,不要望文生义 6 | 2. 如保持英文顺序会影响中文理解,为保证行文流畅,应采用理解优先原则,优先按中文断句 7 | 3. 当说话的人变更时,请加 `>>` 来区分。如:Question? >> Is it? >> Yes. 8 | 4. 出现 Okay,All Right,Now 等语气词,请结合上下文选择合适的翻译,或省略不翻 9 | 5. 学生提问的部分如果听不清楚,字幕也不全([INAUDIBLE]),但是老师回答时候把问题复述了一遍,字幕可译为 `>> [学生提问]` 10 | 6. 出现 [COUGH],[LAUGH],[NOISE],[BLANK_AUDIO],[INAUDIBLE] 等,请自行把握,可结合上下文选择省略不翻译 11 | 7. 卖萌请不要使用字符表情,比如(*3)看起来就像是备注;如果是标注说了三遍,请使用 x3 标记 12 | 13 | ## 格式要求 14 | 15 | 1. 不要合并多条**字幕**,两条字幕之间保持一个空行 16 | 2. 中英文字幕**开头结尾**均不留空格,以 Unix 样式的 LF (Line Feed 的缩写,即 \n) 换行 17 | 3. 省略字幕**开头结尾**不影响表意的标点。如保留问号,但省略如逗号、句号等 18 | 4. 除了 `[`,`]`,和 `>>` 以外使用全角中文标点,如果可能,使用中文数字 19 | 5. 保证每一条字幕中最多两行,一行英文一行翻译,翻译不会过长 20 | 6. 英文和阿拉伯数字同中文之间应当有一个空格 21 | 7. 如遇到英文词汇或阿拉伯数字和标点符号相邻的情况,则不需要再留空格 22 | 23 | 例:就算是最新的 iPad,也不能用 Swift Playgrounds 打包应用。 24 | 25 | ## 错误修正 26 | 27 | 1. 修改错别字,明显的笔误,大小写错误(包括原英文字幕中的错误) 28 | 2. 如能找到对应的 Swift Evolution 编号,以类似(在 SE-0065 之前)… 注明 29 | 3. 如能确定特定的 Swift 语言版本,以类似(在 Swift 4 之前)… 或(对于 Swift 3)… 注明 30 | 4. 如果完全错误,尝试以类似 …(误:原因)或(注:补充)说明。如空间不够,可适当调整上下文。参考第三课 1303 和 420 31 | 32 | ## 翻译术语(Terminology)的要求 33 | 34 | #### 术语的基本处理: 35 | 36 | 1. 尽量和已翻译的内容保持一致 37 | 2. 参考 [术语的特殊处理](#术语的特殊处理) 38 | 3. 参考 [Apple Developer 网页中文版](https://developer.apple.com/cn/) 39 | 4. 参考历年 [iOS 8](https://github.com/X140Yu/Developing_iOS_8_Apps_With_Swift) 和 [iOS 9](https://github.com/SwiftGGTeam/Developing-iOS-9-Apps-with-Swift) 的翻译 40 | 5. 参考 [《The Swift Programming Language》中文版](http://gg.swiftguide.cn/) 及其 [术语表](https://github.com/numbbbbb/the-swift-programming-language-in-chinese/issues/62) 41 | 6. 参考该术语在其它编程语言中的翻译,可以使用 [微软官方术语搜索](https://www.microsoft.com/Language/zh-cn/Search.aspx) 等搜索引擎 42 | 7. 如果以上都没有找到合适的结果,你可以 43 | 1. 在得到任务分配的 issue 下讨论 44 | 2. 直接使用英文原文 45 | 3. 自己决定一个合适的翻译 46 | 47 | #### 术语的特殊处理 48 | 49 | 1. 如果想在同时翻译并保留原文,请根据情况从以下的格式中选用一种 50 | 1. 用逗号隔开。例:这是 Navigator,导航面板 51 | 2. 用括号补充。例:MVC,即 Model(模型)-View(视图)-Controller(控制器) 52 | 2. 不翻译通用的名称,如 MVC,iPhone,Xcode,Storyboard 等 53 | 3. 不翻译直接引用的代码,包括但不限于 54 | - Swift 语言关键字:如 true,false 等 55 | - 类/结构体/元组类型的名称:如 UIView,String 等 56 | - 项目中的代码:如 var description 等 57 | 58 | **后期校对请参考以上标准对术语的使用进行统一** 59 | 60 | ## 补充说明 61 | 62 | > 为何没有完全采用之前的[校对规范](https://github.com/X140Yu/Developing_iOS_8_Apps_With_Swift/blob/master/proofread-rules.md)? 63 | 64 | 其一是因为确实有些因为 Swift 的更新需要修改的地方。其二是因为即使就像之前规则中说的,*“校对是一个很重要的部分,甚至比翻译还要重要”*,但是如果我们从翻译阶段就能做到统一规范,相信校对也能事半功倍。当然,既然这是新的规范,没经过实践验证,欢迎大家根据实际情况讨论后提出修订意见。 65 | --------------------------------------------------------------------------------