IOS 9人机界面指南(三):IOS 技术 (下)

腾讯力作系列:

  1. 《IOS 9人机界面指南(一):UI设计基础》
  2. 《IOS 9人机界面指南(二):设计策略》
  3. 《IOS 9人机界面指南(三):IOS 技术 (上)》

文章索引

  • 3.12 HealthKit
  • 3.13 应用内购买服务(In-App Purchase)
  • 3.14 游戏中心(Game Center)
  • 3.15 iAd富媒体广告(iAd Rich Media Ads)
  • 3.16 无线打印 (AirPrint)
  • 3.17 访问用户数据(Accessing User Data)
  • 3.18 快速查看(Quick Look)
  • 3.19 声音(Sound)
  • 3.19.1 理解用户期望(Understand User Expectations)
  • 3.19.2 定义应用的音频行为(Define the Audio Behavior of Your App)
  • 3.19.3 管理音频中断(Manage Audio Interruptions)
  • 3.19.4 适时处理媒体远程控制事件(Handle Media Remote Control Events, if Appropriate)
  • 3.20 VoiceOver
  • 3.21 路线选择(Routing)
  • 3.22 编辑菜单(Edit Menu)
  • 3.23 撤销与重做(Undo and Redo)
  • 3.24 键盘和输入页面(Keyboards and Input Views)

译者注:本文译自苹果官方人机界面指南 iOS Human Interface Guidelines (2015年10 月21日),由腾讯ISUX设计师翻译整理,非发文者一人之作。译文于ISUX博客,如在阅读过程中发现错误与疏漏之处,欢迎不吝指出。后续章节会陆续更新,敬请期待。

3.12 HealthKit

在iOS 8及之后的版本中,使用HealthKit构建的应用可以利用从健康应用中获取的数据为用户提供更强大、更完整的健康及健身服务。在用户允许的情况下,应用可以通过HealthKit来读写健康应用(用户健康相关数据的存储中心)中的数据。

举例来说,用户可以允许营养应用从健康应用中获取体重及活动数据,用于告诉他们为了达到既定目标一天应该消耗多少卡路里。这个营养应用还可以通过HealthKit更新健康应用上实际消耗的卡路里数据,让用户能更容易地跟踪他们的健康计划的进展。想要了解如何将HealthKit整合进你的应用中,请参阅HealthKit Framework Reference.

下面的指南能够帮助你设计出让人信任且喜爱的健康类应用:

当且仅当你有令人信服的理由时才去访问健康应用中的数据。HealthKit是为了专注于健康及健身服务的应用而设计的。如果一个应用请求获取与其不相关的健康信息,用户不太可能会放心地将个人数据提供给这个应用。因此,你需要确保用户能够理解你的应用需要获取他们某些具体的个人健康数据的原因,并告诉他们共享这些数据的好处。

避免在用户还不知道用途前就向他们请求访问私人健康数据。当用户能够看到当前的任务和你需要访问的数据的关联性时,会更乐意给予你访问权限。举例来说,当用户在给一个减肥应用填写资料时,让他允许你访问健康应用中储存的体重数据是合理的。但如果那个减肥应用在启动时就立即提出访问体重数据的请求,用户更可能会选择拒绝分享该个人数据。

使用系统提供的用户界面来请求访问用户的数据。当用户想要向应用授予访问他们的数据的权限时,一般会期望看到如下图所示的系统权限许可列表。为了确保给用户提供良好的用户体验,应避免在应用的其他页面中重复使用权限许可列表上的信息。而是应该在权限列表中添加些自定义信息来说明为什么你的应用需要访问特定的数据(参阅HKHealthStore Class Feference可获取更多信息)的原因。确保这些信息简洁且能清晰地说明你的应用是如何利用健康应用中的数据,以及收集这些数据的好处。

注意:当用户决定停止与你的应用共享数据时,让他们可以在系统设置中即可完成变更,而不需要通过你的应用界面。

不要在你的应用界面中使用健康应用的图标、图片或者截图。和苹果所有的系统设计一样,这些图像都是受到版权保护的,不应该在你的应用中出现。

不要在你的应用中使用“HealthKit”这个专用术语。HealthKit是代表能够获取健康应用中储存的数据的技术框架的专用技术术语。如果你需要向用户解释你的应用和健康应用中的数据的联系,请使用“健康应用”这个用语。例如,你可以说你的应用“将保存信息至健康应用中”或“所使用的数据是从健康应用中获取的”。

3.13 应用内购买服务(In-App Purchase)

应用内购买服务使得用户可以在你的应用中、你所设计的商店中购买到数字产品。

例如,用户可以做这些事:

  • 将一个应用从基础版本升级到高级版本。
  • 每月订阅新内容。
  • 购买虚拟商品,比如游戏中的等级或道具。
  • 购买并下载新的书籍。

你可以使用StoreKit框架以嵌入的方式将商店添加到你的应用中,并且用来支持应用内购买服务。当用户进行购买时,StoreKit会连接到应用商店进行安全支付,然后再告知你的应用以便它可以提供用户已购买的商品。

重要:应用内购买服务只提供支付功能,其他功能由你自己提供,例如向用户展示商品,解锁内置功能,从你自己的服务器上下载内容等等。当然,你所提供的所有商品都必须在应用商店注册过。

想要了解关于在应用中添加商店的技术要求,请查看In-App Purchase Programming Guide.想要了解更多关于应用内购买的商业需求信息,请查看App Store Resource Center.当然,你还应该查看相关许可协议来确定你的应用可以出售哪些商品以及如何提供商品。

遵循以下几点规范,可以帮助你设计出用户喜欢的购买体验。

将商店的使用体验优雅地集成到你的应用中。在展示商品和处理交易时,给用户提供一种熟悉、一致的体验。你一定不希望用户在访问你的商店时感觉像是进入别的应用。

使用简单明了的标题和说明。最好能让用户在扫过一组项目时,可以快速发现感兴趣的内容。文案上不要截断隐晦,简单直白的语言和标题更容易让用户理解你所要展示的商品。

不要更改默认的确认对话框。当用户购买一个商品时,StoreKit会提供一个确认对话框(如上图所示)。这个确认对话框可以帮助用户避免买错东西,所以不要修改它。

3.14 游戏中心(Game Center)

游戏中心给用户提供玩游戏、组织多人在线游戏以及其他更多功能。玩家可以使用内置的游戏中心应用来注册账户、发现新游戏、添加好友、浏览玩家排名和战绩。

作为一名游戏开发人员,你可以使用GameKit应用接口来发布分数和战绩到游戏中心的服务器上,在你的游戏页面中显示玩家排名,帮助用户找到其他玩家。想要了解如何将游戏中心集成到你的应用中,请查看Game Center Programming Guide.

遵循以下几点规范,有助于你的应用给用户提供好的游戏中心体验。

不要使用自定义的用户界面来提示用户登录到游戏中心。如果用户在未登录到游戏中心的情况下打开了一个需要启用游戏中心的应用,系统会自动提醒他们去登录。所以没必要自定义一个登录界面,而且有可能还会让用户感到困惑。

一般情况下,使用标准的游戏中心界面。在少数情况下,可能自定义游戏中心的界面是合理的,但是这样做会有让用户感到困惑的风险。标准的游戏中心界面对于iOS和OS X的用户是熟悉的,而且它会给用户一种置身于一个庞大游戏社区的感觉。

允许用户关闭语音聊天。有些用户可能不想在进入游戏时就自动开启语音聊天,而且大多数用户希望在特定情境下可以关闭语音聊天。

3.15 iAd富媒体广告(iAd Rich Media Ads)

如果你允许你的应用中出现广告,那么你可以通过用户浏览或者点击这些广告获得收益。(如图所示,这个底部预留位置就是用来放置iAd横幅广告。)

通过iAd网络你可以在你的用户界面中以特定的视图投放一则广告。最初,这种视图可以用来承载目标横幅广告,起到引导用户进入查看全面广告详情的作用。当用户点击该横幅广告时,广告就会执行预先设定的动作,例如播放一段影片、展示可交互的内容,或者启动Safari打开目标网页。该动作所展示的内容可以遮挡住你当前的用户界面,或者使你的应用转换到后台运行。

有三种类型的横幅广告供你在应用中使用:标准(standard)、中等矩形(medium rectangle) 和全屏(full screen)。所有类型的横幅虽然在外观和行为上存在差异,但都提供同样的功能,就是引导用户进入广告。

标准横幅(standard banner)占用屏幕较少的空间,通常从始至终都可见。你可以选择在应用的哪些页面展示标准横幅,并在给这些页面设计布局时预留出空间。

ADBannerView类中的广告视图来显示标准横幅广告。

中等矩形横幅(medium rectangle banner)的行为同标准横幅类似,同样也可以选择展示中等矩形横幅的位置。

中等矩形横幅只能在iPad的应用中使用。你可以使用ADBannerView类中的广告视图来显示中等矩形横幅广告。

全屏横幅(full screen banner)会占用屏幕的大部分甚至是全屏空间,并且通常只在应用程序流的特定时间或特定位置显示。你可以选择使用模态视图来显示横幅广告,或者用独立页来展示可滚动的广告内容。(在下面的示例中,应用提供了一种杂志阅读的体验,通过翻页离开或回到全屏广告页面。)

你可以使用ADInterstitialAd类中的广告视图在你的应用中显示全屏横幅广告。

iAd框架包含了所有类型的横幅广告,并且会在右下角显示iAd的标识。iAd框架的设计固定在屏幕底部时看起来效果最佳。

为了保证广告无缝植入,并且要提供最好的用户体验,可以遵循以下几点规范。

将标准横幅广告视图尽量放置在屏幕底部或底部附近。这个位置的差别取决于屏幕底部是否包含栏(bar)以及是什么样的栏。

栏 标准横幅的位置 屏幕底部没有栏 屏幕底部 屏幕任何地方都没有栏 屏幕底部 有工具栏(toolbar)或标签栏(tab bar) 底部栏的上方

将中等矩形横幅广告视图放置在不会干扰内容的地方。和标准横幅一样,中等矩形横幅也最好放置在屏幕底部或底部附近。放在底部附近也能减少干扰用户的可能性。

当用户体验存在中断时请使用模态视图来展示全屏横幅广告。如果你的应用中有自然中断或情景转换,用模态样式来展示会更合适。当你使用模态样式来展示全屏横幅时(通过用presentFromViewController实现),用户要么进入广告,要么关闭它。出于这个原因,当用户有做出转变的预期时 (比如完成了一个任务后) 用模态视图的形式来展示比较好。

应用的界面视图进行转场切换时不要使用模态样式展示全屏横幅。如果用户在使用你的应用时会频繁的进行屏幕切换操作,例如杂志翻页或翻阅一些画册图片合集,此时使用非模态的形式会更合适。当你使用非模态来显示全屏横幅时(通过使用presentInView实现),可以在用户界面中保留栏 (bar) 使得用户可以通过应用中的控件进入或退出广告。同其他横幅广告一样,点击全屏横幅广告也会触发iAd体验,但是如果条件允许的话,你的应用也可以对横幅广告区域支持其他手势操作 (比如拖动或滑动)。

确保使用合适的动画效果来显示和隐藏非模态的全屏横幅视图。例如,杂志阅读应用可以用和杂志翻页一样的动画效果。

确保横幅广告在应用中出现的时间和位置是合理的。用户只有在不觉得广告会打扰他们正常的工作流程时才有可能去体验iAd.这点对于游戏这样的沉浸式应用尤其重要:你肯定不想将横幅放置在影响用户玩游戏的位置。

避免将横幅放置在用户只会一扫而过的页面。最好不要将横幅广告放置在用户会快速略过的页面,比如用户正要深入挖掘或前往他们所的内容。通常用户在一个页面停留至少1、2秒后才有可能会点击广告。

尽可能的支持双向展示横幅广告。最好让用户在使用应用时不必旋转设备就能浏览广告。当然,支持双向也能给你的广告提供更大的展示区域。想要了解如何确保转换方向时横幅广告能正常响应,请查看iAd Programming Guide.

不要让标准或中等矩形横幅广告滚出屏幕。如果你的应用需要滚动来展示更多内容,确保横幅广告一直固定在它的位置上。

当用户浏览或与广告进行交互时,暂停那些吸引用户注意力或需要操作的活动。当用户选择浏览广告时,他们不想因此错过应用中正发生的事件,也同样不想让应用打断广告体验。一个好的方法是像应用程序转入后台运行那样暂停当前活动。

除非有特殊情况,否则不要中断广告。一般情况下,当用户浏览并与广告进行交互时,应用还是会继续运行并接收事件,所以也有可能突然出现一个事件需要获得用户的注意力。然而,需要打断广告的场景其实非常少。有一种情景是有的应用会提供互联网语音协议服务(VoIP).在这种应用中,有电话接入时可能会取消正在运行的广告。

注意:取消广告可能会对应用能接受的广告类型以及能获取的收益有不好的影响。

3.16 无线打印 (AirPrint)

用户可以通过AirPrint无线打印应用中的内容,还可以使用打印中心应用检查打印任务。

你可以利用内置的支持程序来打印图片和PDF文件,或者可以使用特定的打印程序接口来完成自定义的格式设置和渲染设置。iOS可以处理打印机的发现、任务排序以及在指定打印机上执行打印任务。

通常来讲,用户想要打印文件的时候,只需要点击应用中的标准动作按钮(Action button)。当他们在界面视图中选择了要打印的项目后,可以接着选择打印机,设置打印属性,最后点击打印按钮开始打印。

打印中心应用是一个只有在处理打印任务时才可见的后台系统应用,用户可以用它来查看打印任务。用户可以在打印中心浏览当前打印队列,查看某个打印任务的详情,还可以取消某个任务。

只需添加少量代码就可以支持基本打印功能 (想要了解在代码中添加打印功能,请查看Drawing and Printing Guide for iOS).想要确保好的打印体验,可以遵循以下几点规范:

使用系统提供的动作按钮。用户对系统提供的按钮的含义和行为都很熟悉,所以尽可能的使用系统动作按钮。如果你的应用没有工具栏或导航栏,那就要另当别论了。在这种情况下,你就需要自己设计一个可以出现在应用主界面的打印按钮,因为动作按钮只能在工具栏和导航栏中使用。

在当前情境下打印操作是基本功能时才显示打印项(Print item).如果当前情境并不适合打印,或者用户并不想打印,就不要在由动作按钮显示的视图中将打印项显示出来。

合适的话,给用户提供更多打印选项。例如,让用户设置打印页码范围或打印份数。

如果用户不能打印,则不要显示特定的打印用户界面。在向用户展示有打印项的界面前,确保用户的设备是支持打印的。想要了解如何在代码中实现,请查看UIPrintInteractionController Class Reference.

3.17 访问用户数据(Accessing User Data)

位置服务允许应用获取用户当前大致的地理位置,设备指向的方向以及用户移动的方向。其他系统服务,例如通讯录、日历、备忘录和相册等,同样也允许应用访问用户存储在里面的数据。

虽然获取了用户数据的应用能带来一定的方便,但还是需要为用户提供维持信息私密性的功能。例如,用户喜欢应用自动给内容加上位置标签,或者可以找到附近的好友,但用户也需要能在不想分享位置的时候关闭这些功能。(想要学习如何给应用增加获取位置功能,请参阅Location and Maps Programming Guide.)

以下几点可以帮助您以用户不反感的方式获取用户数据。

确保使用户理解分享私人数据的原因。如果没有明显的需要,用户自然会对私人信息的请求感到怀疑。为了避免用户反感,确保在用户使用明显需要个人信息的功能时再进行提醒。例如,即使没有打开位置服务用户也可以使用地图,但是在用户使用定位或导航功能时就会有提醒。

应用需要个人信息的原因不明显时向用户做出解释。你可以在提醒框中给出文字性的描述,例如“这个应用需要访问你的通讯录”或者“是否允许应用获取你的地理位置?”。这些文案最好明确且有礼貌以让用户无压力的理解为什么需要访问他们的信息。

讲述原因的文案应该遵循以下原则:

  • 不要包含你的应用名称,因为系统提供的提醒框已经包含了。
  • 清楚地描述你的应用为什么需要这些数据。如果可以的话,你也可以解释不会用这些数据做什么。
  • 使用以用户为中心的术语并且进行本地化。
  • 在易于理解的情况下越短越好。尽可能避免超过一句话。
  • 使用句式大小写(sentence-style capitalization).(句式大小写指的是第一个单词大写,除了专有名词和专有形容词以外的词都小写。)

只有当你的应用没有用户数据就无法提供基础服务时,才在一开始就征求用户的许可。如果你的应用在知道了用户私人信息后才能提供主要功能是显而易见的话,用户不会因此觉得烦扰。

避免在用户选择需要数据的功能之前调用触发提醒框的程序。这样,就可以避免用户疑惑为什么在使用不需要私人数据的功能时有请求提醒。(注意,检查用户位置服务的设置并不会触发提醒。)

检查位置服务的设置来避免触发没必要的提醒。你可以使用核心位置的程序接口来实现(想要学习如何做,请参阅Core Location Framework Reference).使用这些知识,可以尽可能地在使用需要位置信息的功能时才进行提醒,或者完全避免提醒。

3.18 快速查看(Quick Look)

通过使用Quick Look,用户可以在你的应用内预览文件,即使你的应用是打不开这个文件的。举例来说,你可以允许用户预览一些从网站上下载或从其他来源获得的文件。

想要学习如何在应用中加入Quick Look文件预览功能,请参阅Document Interaction Programming Topics for iOS.

在你的应用内预览文件之前,用户可在你定制的视图中查看该文件的信息。例如,用户从一封邮件中下载了附件之后,邮件应用(Mail)会在邮件中使用定制的视图展示文件的图标、标题和大小。用户可以通过点击它来预览文件。

你可以在应用中用一个新的视图来展示文件预览,或者使用全屏模态视图。展示的形式取决于你的应用运行在什么设备上。

在iPad上使用模态视图来显示文件预览。iPad的大屏幕适合在一个方便用户离开的沉浸式环境中展示文件预览。缩放操作(zoom transition)很适合展示预览。

在iPhone上使用专用的视图,最好是导航视图来显示文件预览。这样可以使用户在应用情境中通过导航进入文件预览,不至于迷失。虽然也可以在iPhone应用中使用模态显示,但不推荐这样做。(注意缩放操作在iPhone上并不适用。)

另外要注意的是,在导航视图中显示文件预览意味着允许Quick Look在导航栏上放置特定的预览控件。(如果你的视图中包含工具栏,Quick Look会将预览控件放在工具栏上。)

3.19 声音(Sound)

无论声音在你的应用中是主要体验的一环,还是锦上添花的元素,你都需要知道用户对声音表现的期望以及与如何满足这些期望。

3.19.1 理解用户期望(Understand User Expectations)

人们可以使用设备控件来调整声音,他们还可能使用有线或无线的耳机和听筒。人们也会对于他们的行为如何作用于他们听到的声音有各种各样的期望。虽然你可能会发现有一些期望很让人意外,但它们都会遵循用户控制的原则,即应是用户而非设备掌控听到声音的时机。

用户会依据需要将设备静音:

  • 避免被突兀的音效打断,比如手机铃声和信息接收音等
  • 避免听到用户操作所产生的副产品的声音,比如键盘或其他反馈音、偶然的声音或应用启动的声音
  • 避免听到那些在玩游戏时非必要出现的声音,如音效和配乐

例如,在剧院中,用户将他们的设备调至静音以避免打扰剧院中的其他人。在这一情境下,用户仍然希望能在他们的设备上使用应用,但他们不希望被无预期或突兀的声音所打断,如手机铃声或新消息音。

当用户操作的明确目的就是听到声音时,铃音/静音开关(或静音开关)不会屏蔽这些操作所产生的声音。例如:

  • 在仅有媒体播放功能的应用中的进行媒体播放是不会被静音的,因为播放媒体是用户明确期望的。
  • 闹钟不能被静音,因为闹钟是用户明确设定使用的。
  • 语言学习应用中的音效素材不能被静音,因为用户进行了明确的操作希望听到它。
  • 音频对话应用中的对话不被静音,因为用户打开这个应用的唯一目的就是进行音频对话。

用户使用设备音量调节按键可调节他们的设备所能发出的所有声音的音量,包括歌曲、应用音效和设备声音。不管铃声/静音(或静音)的开关在什么位置,用户都能使用音量调节按键屏蔽所有声音,使用音量调节按键调节应用当前所播放的音频时同样调整了全局系统的音量,铃声音量除外。

对于iPhone:当没有音频播放时使用音量键可以调整铃声音量。

用户使用耳机的目的在于能够私密地收听声音以及解放他们的双手。不管这些配件是有线的还是无线的,用户对这个体验都有特定的期待。

当用户插入耳机或连接无线音频设备时,他们期望能以私密的状态继续收听当前播放的音频。因此,他们希望应用能够不中断地继续播放当前正在播放的音频。

当用户拔出耳机或断开与无线设备的连接时(抑或设备超出范围或关闭时),他们不希望他们刚刚收听的内容被自动地与他人分享。因此,他们希望正在播放音频的应用暂停播放,让他们能够在自己想要继续播放的时候再开启。

3.19.2 定义应用的音频行为(Define the Audio Behavior of Your App)

如果必要的话,你可以通过调整相关的、独立的音量水平以在你的应用中制造最好的混音输出效果。但最终音效输出的音量也应该能由系统音量控制,可以通过音量键或音量滑块进行调节。这意味着应用的音频输出的控制权仍然归属在用户手中。

确保你的应用能适时地显示音频路径选择。(音频路径(audio route)是指音频信号的电子通路,例如从设备到耳机或是从设备到扬声器。)即使人们没有物理性的插入或拔出音频设备,他们也仍希望能选择其他不同的音频路径。为了实现这一诉求,iOS能自动显示可让用户选择输出音频路径的控件(使用MPVolumeView类能允许这个控件显示在你的应用中)。由于选择不同的音频路径是用户主动的行为,用户期望当前播放的音频能继续不中断。

如果你需要显示音量滑块,在使用MPVolumeView类时,确保使用的是系统提供的可用的音量滑块。注意,当正在使用的音频输出设备不支持音量控制时,音量滑块会被合适的设备名称所替代。

如果你的应用只产生一些与其功能无必要关系的界面音效时,(尽量)使用系统音效服务(System Sound Services)。系统音效服务是一种能产生警示音、界面音效和发出振动的iOS技术;它不适合任何其他用途。当你使用系统音效服务(System Sound Services)来产生音效时,你不能干涉你的音频与设备的音频的交互方式,也不能干涉它处理干扰和设备配置变化的方式。想了解如何使用这一技术,请参阅Audio UI Sounds (SysSound)中的范例项目。

如果音效在你的应用中扮演重要的角色,使用音频会话服务(Audio Session Services)或是AVAudioSession类。这些程序接口不产生音效;相反,它们会帮助你了解你的音频应该如何与设备的音频进行交互以及如何响应设备配置的干扰与变化。

对于iPhone:无论你使用什么样的技术来制作音频,无论你如何定义来它的行为,电话总是可以中断当前运行的应用。这是因为任何应用都不应该阻止人们接收来电。

在音频会话服务(Audio Session Service)中,音频会话(audio session)执行了你的应用与系统之间音频中介的功能。音频会话中最重要的方面之一就是类目(category),它定义了你的应用的音频行为。

为了实现音频会话服务带来的好处并提供用户期望的音频体验,你需要选择可以完美描述应用音频行为的类目(category)。具体情况取决于你的应用只在前台播放音频还是也要在后台播放音频。在你做这一选择的时候,遵循以下这些原则:

  • 依据其语义而非精确的行为来选择音频会话类目。通过选择目的清晰的类目,你可以确保你的应用能表现得符合用户期望。除此之外,当以后行为的精确集合被重新定义时,它可以为你的应用提供最佳的机会使其合理运行。
  • 在极少数情况下,可以添加属性到音频会话中以修正一个类别的标准行为。一个类别的标准行为代表多数用户的期望,因此在你改变那个行为之前你应该仔细地考虑。例如,你可以添加闪避(ducking)属性以确保你的音频声音能比其他所有的音频都大(除了电话音频),如果这就是用户所期望的。(欲了解更多关于音频会话属性的内容, 请参见的Fine-Tuning the Category。)
  • 依据设备当前的音频环境来考虑你的类目选择。这应该是合理的,举个例子,用户可以在使用你的应用的同时听其他音频而非你的配乐。如果要这样做,须确保避免当你的应用启动时,迫使用户停止收听当前的内容或要需要额外地在两者之间做出选择。
  • 通常情况下,避免在你的应用运行时改变类目。改变类目的首要依据是你的应用是否需要在不同的时机支持录音和播放。在这种情况下,更好的选择是依据需要在录音类目与播放类目之间转换,而非同时选择播放和录音类目。这是因为选择录音类目可以确保正在录音时不会听到提示音,比如收到信息的提示音。

表35-1列举了你可以使用的音频会话类目。不同的类目可以允许通过铃声/静音开关或静音开关(或设备锁)来实现静音、与其他的音频混合或者控制应用在后台播放。(欲了解编程界面上所呈现的类目和属性的准确名称,请参见Audio Session Programming Guide.)

表35-1 音频会话类目及其相关行为

类目 意义 静音 混合 后台播放 个人环境 声音增强了应用的功能且应该静音其他音频 支持 不支持 不支持 环境 声音增强了应用的功能但不应该静音其他音频。 支持 支持 不支持 播放 声音对应用来说很重要且可以与其他音频混合。 不支持 不支持(默认)支持(当“与其他音频混合”属性被添加时) 支持 录音 音频是用户记录的。 不支持 不支持 支持 播放和录音 声音代表音频输入与输出,按顺序地或同时地。 不支持 不支持(默认)支持(当“与其他音频混合”属性被添加时) 支持 音频处理 应用执行硬件辅助音频编码(不播放或录音)。 不适用 不支持 支持*

*如果你选择音频处理类目并且你希望在后台运行音频进程,你需要在完成音频处理之前防止你的应用被暂停。欲了解如何实现这一功能,参见iOS应用编程指南》中的执行长时间运行的后台任务。

以下是一些示例情境,其中指示了如何选择音频会话类目以提供用户喜欢的音频体验。

情境1:一个帮助人们学习新语言的教育类应用。你需要提供:

  • 用户点击特定控件时播放反馈音效
  • 当用户想听到正确发音的示例时播放字词的录音

在这个应用中,声音对于主要功能是十分重要的。人们使用这个应用来听他们正学习的语言的词语与短语,因此即使当设备锁定或者被调至静音时也要能播放声音。因为用户需要清晰地听到声音,他们会期望其他他们可能播放的音频都被静音。

为了满足用户对于该应用所期望的音频体验,你应该使用播放(Playback)类目。虽然这一类目可以被定义为与其他音频混合,但该应用应该使用默认的行为以确保其他的音频不会干扰那些用户明确选择听到的教育性内容。

场景2:网络协议电话(VoIP)应用。你需要提供:

  • 接收音频输入的能力
  • 播放音频的能力

在该应用中,声音对于主要功能是十分重要的。人们经常会在使用另一个应用时使用该应用与他人进行交流。用户期望能在他们将设备调至静音或设备被锁定时接听电话,他们希望在来电期间其他音频被静音。他们也希望应用在后台运行时也能继续打电话。

为了满足用户对于该应用所期望的音频体验,你应该使用播放和录音(Play and Record)类目,并且你要确保只有在你需要时才会激活你的音频会话,以便用户可以在打电话过程中使用其他音频。

场景3:允许用户在不同任务中操作角色的游戏。你需要提供:

  • 不同的游戏运行音效
  • 配乐

在该应用中,声音会在很大程度上提升用户体验,但对于主任务并没有那么重要。而且,用户可能会希望能在玩游戏时静音或听他们乐单中的歌曲而不听游戏配乐。

最好的策略是在你的应用启动时确定用户是否在收听其他音频。不要要求用户选择他们是要收听其他音频或是你的音效。而应该使用音频会话功能中的AudioSessionGetProperty来请求kAudioSessionProperty_OtherAudioIsPlaying属性的状态。依据所请求的答案,你可以选择环境(Ambient)或是个人环境(SoloAmbient)类目(这两种类目都允许用户静音玩游戏):

  • 如果用户正在听其他音频,你应该假设他们想要继续听并且不想被强迫收听游戏音效。在这种情境下,你最好选择环境(Ambient)类目。
  • 如果用户在你的应用启动时没有在收听其他音效,你最好选择个人环境(SoloAmbient)类目。

情境4:一个为用户到达目的地提供准确、实时导航指示的应用。你需要提供:

  • 路途中每一步的语音指示
  • 一些反馈音效
  • 支持用户继续收听他们自己的音频的能力

在该应用中,无论应用是否是在后台运行,语音导航指示都表现为主要任务。基于这一原因,你最好使用播放(Playback)类目,它允许你的音频在设备被锁定、静音或是在后台运行时仍可以播放。

你可以通过添加kAudioSessionProperty_OverrideCategoryMixWithOthers属性来实现允许人们在使用你的应用时收听其他音频。但是你也想要确保用户在他们正在播放其他音频时能听到语音提示。你可以为音频会话添加kAudioSessionProperty_OtherMixableAudioShouldDuck属性来确保你的音频比其他音频的声音更大( iPhone上的电话音频除外)。这些设置允许应用在后台运行时也可以恢复音频会话,可以确保用户能获得实时更新的导航。

情境5:一个允许用户上传文本和图片到网站上的博客应用。你需要提供:

  • 简短的启动音效文件
  • 伴随用户行为产生的各式各样的短音效(例如当邮件被上传后播放的音效)
  • 发送失败时播放的提示音

在该应用中,声音提升了用户体验,但也不是必需的。主任务与音频并没有关系,用户也不是必须要通过收听声音才能成功使用应用。在这一情境中,你最好使用系统声音服务来产生声音。这是因为这个应用中所有声音的音频情境都符合本技术想要达到的目的,也就是说应制作符合用户所期待的、能通过设备和铃声/静音(或静音)开关来调节的界面音效和提示音。

3.19.3 管理音频中断(Manage Audio Interruptions)

有时候,当前播放的音频会被来自于不同应用的音频所打断。举个例子,在iPhone上,来电会持续中断当前应用的音频。在多任务情境中,这种音频中断的频率可能会很高。

为了提供用户喜欢的音频体验,iOS系统依赖于你能做到下面几点:

  • 识别可能会引起应用中断的音频类型
  • 当应用在音频中断结束后继续运行时进行合理地反馈

每个应用需要识别会引起音频中断的类型,但不是每个应用都需要决定如何在音频中断结束后进行反馈。这是因为多数类型的应用应在音频中断结束后恢复音频。只有那些主要或部分由媒体播放组成(以及提供媒体播放控制)的应用,才必须用额外的步骤来决定什么是合适的反馈。

从概念上讲,基于中断当前音频的音频类型以及中断结束后用户所期望的特定的应用反馈方式,有两种类型的音频中断:

  • 可恢复性中断是(resumable interruption)被用户视为临时穿插在他们的主要聆听体验中的音频引起的。

在可恢复性中断结束后,有媒体播放控件的应用应该恢复它被中断前的任务,无论是继续播放音频还是保持暂停。没有媒体播放控件的应用则应该恢复播放音频。

举个例子,试想用户在iPhone上使用应用播放音乐时,在播一首歌的中间来了一个网络电话。用户接起了电话,期望在他们通话时播放的应用能静音。在通话结束后,用户希望播放的应用自动恢复播放歌曲,因为音乐而非电话才是他们的主要聆听体验,而他们在电话接入前也没有暂停音乐。另一方面,如果用户在电话接入前暂停了音乐播放,他们会希望电话结束后音乐仍保持暂停。

其他能引起可恢复性中断的应用的例子还有那些具备闹钟、音频提示(例如语音方向指示)或其他间歇性音频功能的应用。

  • 不可恢复中断(nonresumable interruption)是由那些被用户视为首要听觉体验的音频所引起的,比如媒体播放产生的音频。在不可恢复中断结束后,显示媒体播放控件的应用不应该恢复播放原来的音频。而没有媒体播放控件的应用应该恢复播放音频。例如,假设用户正在收听一个音乐播放应用(音乐应用1),此时另一个音乐播放应用(音乐应用2)打断了它。用户终止后决定收听音乐应用2一段时间。在退出音乐应用2之后,用户不想要音乐应用1自动恢复播放,因为此时他们主动将音乐应用2作为首要的听觉体验。

下面的指南可以帮助你决定当一个音频中断后如何继续以及提供什么信息:

确定由你的应用引起的音频中断的类型。在你的音频结束时,你可以通过以下任意一种方式去禁用你的音频会话来做到这一点:

  • 如果你的应用引起了一个可恢复性中断,使用AVAudioSessionSetActiveFlags_NotifyOthersOnDeactivation标识禁用你的音频会话。
  • 如果你的应用引起了一个不可恢复中断,不用任何标识就可以禁用你的音频会话。

无论提供与否,标识会在适宜的情况下允许iOS系统赋予被中断的应用自动恢复播放它们的音频的能力。

决定是否应该在一个音频中断结束后恢复音频。你应依据你应用中所提供的音频体验来做这一决断。

  • 如果你的应用给用户呈现了用于播放或暂停音频的媒体播放控件,你需要在一个音频中断结束后检查AVAudioSessionInterruptionFlags_ShouldResume标识,如果你的应用接受应该恢复(Should Resume)标识,你的应用应该:
  • 恢复播放音频(你的应用被打断时在主动播放音频)
    ·不恢复播放音频(你的应用被打断时没有在主动播放音频)
  • 如果你的应用没有呈现任何用户可用于播放或暂停音频的媒体播放控件,你的应用无论是否有“应该恢复”标识,都始终应在音频中断结束后恢复之前播放的音频。例如,播放配乐的游戏应该在被中断后自动恢复播放配乐。

3.19.4 适时处理媒体远程控制事件(Handle Media Remote Control Events, if Appropriate)

当人们使用iOS媒体控制器或辅助控制器(如耳机线控)时,应用要能响应远程控制。使你的应用能接收来自于你的用户界面之外的输入,无论你的应用当前是在前台还是后台播放音频。

应用可以在播放媒体的过程中,通过后台向支持Airplay的硬件(如Apple TV)发送视频。这样的应用可以接收通过远程控制事件实现的用户输入行为,因此用户可以控制处于后台运行状态的应用中的视频播放。除此之外,这类应用在后台运行时也能恢复被中断的音频。

当一个媒体播放应用在后台播放音频或视频时,尤其需要合理响应媒体远程控制事件。

当你的应用在后台运行时,为了满足与播放媒体特权相关的责任,要确保遵循以下这些原则:

限制你的应用接收远程控制事件的次数。例如,当你的应用可以帮助用户阅读内容、搜索信息或是收听音频时,它只有在用户处于音频场景中时才应该接收远程控制事件。当用户脱离音频情境时,你应该放弃接收事件的能力。如果你的应用允许用户在支持AirPlay的设备上播放音视频,它应该在媒体播放期间都可以接收远程控制事件。遵循这些原则能使用户在你的应用中处于非媒体情境中时,通过耳机控制获得另一个应用的媒体体验。

尽可能地使用系统原生的控件以提供AirPlay支持。当你使用MPMoviePlayerController类实现AirPlay播放功能时,你可以利用标准的控件使用户可以选择当前范围内支持AirPlay的硬件。或者你可以使用MPVolumeView类来显示用户可选择的支持AirPlay的音频或视频设备。用户习惯于这些标准控件的外观和行为,因此他们可以理解如何在你的应用中使用它们。

不要改变事件的用途,即使这个事件在你的应用中没有意义。用户期望iOS系统的所有应用媒体控制和辅助控制能有功能上的统一。你不必实现你的应用所不需要的那些事件,但你所实现的事件必须产生符合用户期望的结果。如果你重新定义一个事件的意义,你会使用户困惑并冒险把他们带入一个未知的状态,他们只能通过退出你的应用来逃离。

3.20 VoiceOver

VoiceOver增加了对盲人、弱视用户以及一些有特定学习困难的用户的可用性。

为了确保VocieOver的用户能使用你的应用,你需要在你的用户界面中提供一些有关视图和控件的描述信息。对VoiceOver的支持不需要你改变你用户界面内的任何视觉设计。

当你完全遵照标准的方式使用标准的用户界面元素时,几乎不(即使有也很少)需要增加额外的工作。你的用户界面越趋向定制化,你就越需要提供更多的信息来保证VoiceOver能准确的描述你的应用。

增加你的iOS应用对VoiceOver用户的可用性,可以扩大你的用户基础并帮助你进入新的市场。支持VoiceOver也可以帮助你遵守由主流群体所制定的可用性指导准则。

3.21 路线选择(Routing)

地图可以显示到达用户目的地的可选路线:

当人们想要获得关于某条路线的更多交通信息时,地图也可以显示能提供路线选择的应用列表(包括安装在设备上的应用也包括应用商店中的应用)。

路线选择应用可以提供当前选择的路线有关的信息。人们希望路线选择应用能够快捷、易用,特别是保证准确性。依据本章提供的指导原则能帮你为用户提供他们可信任的交通信息和他们期望的用户体验。

重要:地图能依据人们选择的路线给他们提供驾车和步行的指示。路线选择应用可以提供交通信息,它着重于使用交通工具(如公交车、火车、地铁、渡船、自行车、行人、穿梭巴士等)的模型替代实物逐步地指示方向。

如果你的应用不能提供用户指定的路线的交通信息,那么不要将你的应用定位为路线选择应用。

实现你的应用所承诺的功能。当人们在交通列表里看到你的应用时,他们认为它可以帮助其到达目的地。但是如果你的应用不能提供所选路线的信息,或者它没能涵盖它看似应该涵盖的那些种类的交通信息,人们就不会愿意给它第二次机会。准确的表达出你的应用的能力是十分重要的;否则,你的应用会看起来像是在故意误导用户。

在你的路线选择应用中,有两种主要的方式可以给用户信心:

  • 尽可能准确的定义你所支持的地理区域。例如,如果你的应用能帮助人们获得巴黎的公交线路的信息,那你所支持的地区应该是巴黎,不是法兰西岛,也不是法国。
  • 明确你所支持的交通信息类型。举个例子,如果你专攻于地铁信息,不要暗示你仿佛支持所有的轨道交通类型

注意:虽然准确的报告你所支持的地区可能意味着会减少你的应用在交通信息列表里的出现次数,但这么做却可以帮助用户更加信任它。

为易用性合理组织界面。易用性对于路线规划应用来说特别重要,因为用户常常会在极具挑战性的情况下使用它们——例如在明亮的阳光下、在昏暗的车厢内抑或是在颠簸的旅程中,或在非常紧急的情况下。要确保你的文字在任何光照条件下都能容易的阅读,确保按钮即使在并不平稳的旅程中也能易于准确点击。

专注于路线。虽然辅助信息会很有用,但你的应用应该专注于为用户提供逐步的指示以便他们能据此到达目的地。特别要强调的是,你要让用户知道他们处于哪一步,并知道如何到达下一步。你可以提供额外的数据(比如时间表或系统地图),但不要让这些数据比交通信息还重要。

为路线的每一步提供信息。永远不要让用户感觉被你的应用所遗弃。即使在可以准确的报道你所支持的地区时,你也不能假定用户已经抵达的路线中的第一个交通节点或是最后一个交通节点就是他们目的地点。为了控制这一情况,首先就是测量起点到终点距离。如果距离足够短,要提供从用户当前位置到第一个交通节点及从最后一个交通节点到用户目的地的步行方向指示。如果步行不是一个合理的选择,尝试描绘用户的其他选项。如果必要的话,你可以给用户提供打开地图,获取这部分路线的步行或驾车方向指示的方式。

当用户从地图应用切回你的应用时,不要要求他们重复输入信息。如果用户从地图应用切入(你的应用)时,你已经获知了他们中意的起点与终点,因此你可以在应用打开时直接呈现适合的交通信息。如果用户从主屏幕中开启你的应用,要为他们提供简洁的方式用以输入路线详情。

显示图文并茂的交通信息。地图页面可以帮助人们以更大的、实物性的视角查阅他们完整的线路;清晰的步骤可以帮助人们专注于他们抵达目的地所需采取的必要行动。最好可以同时支持这两个任务并能让用户便捷地进行切换。

注意:无论以什么格式,最重要的是显示与用户线路相关的相同的交通信息。例如,如果路线中包含五个步骤,在地图和路线列表页中必须描绘相同的五步。

当你的应用被从交通列表中选中时,需要以显示完整的线路做为良好的开始(包括在地图页面中显示始于或抵达交通节点的步行路线)。地图页面可以为用户提供他们旅途的多步骤的总览,并能展示适于周遭地理环境的路线。

用附加信息丰富地图页面。人们期望你的应用中的地图可以表现的与他们使用过的其他地图相似。除了用户能放大和缩小以外,你还应该显示用户所需的那些注释信息。例如,你应该显示图钉用以代表用户当前的位置、目的地以及沿路的转乘点或重要的节点。

确保避免只显示一个单独的图钉,因为对用户来说,如果没有额外的背景,很难理解它代表什么。欲了解在你的应用中使用地图页面的更多信息,请参阅Map View.

尽可能地整合静态地图页面,例如在地图视图中加入地铁系统地图等。一个很好的实现方法就是在地图页面覆盖静态图片,以便用户可以看到他们的路线及他们的当前位置是如何与更大的交通系统相关联的。

注意:如果你决定让应用显示一个静态的地图图片,要确保使用高分辨率的图片以保证用户在缩放时维持高质量的显示。

给用户提供不同的方案来挑选多样的交通选择。很多因素会影响人们交通的选择,例如不同的时间段,天气以及他们携带东西的多少,所以提供简洁的不同交通方式的对比是十分重要的。例如,你要能让用户可以依据开始或结束的时间、需要步行的数量、沿途停下的次数抑或转乘的次数或所需的不同的交通类型等来挑选交通方式。不管你显示多种交通选择的顺序如何,确保用户能立即分辨出这些选项的不同之处。

考虑使用推送通知为人们提供与路线相关的重要信息。尽可能的让人们了解交通情况信息的变化,以便他们可以据此调节自己的计划。例如,如果火车晚点或者巴士路线临时停滞,人们可能会需要选择不同的交通路线到达目的地。而在一条不同步骤的站点之间相隔很长距离的交通路线中,人们会希望在他们的交通工具将要抵达行程中的下一部分时能获得通知。

3.22 编辑菜单(Edit Menu)

用户能呼出一个编辑菜单来完成诸如在文本视图、网页或图片视图中的剪切、粘贴以及选择操作。

你可以通过调整一些菜单的行为使用户对你应用中的内容有更多的控制权。举个例子,你可以:

  • 列举出适用于当前情境的标准菜单的命令
  • 在菜单显示前判定菜单的位置,以避免你应用的用户界面中的重要信息被遮盖
  • 定义当用户双击展开菜单时会被默认选中的对象

你不能改变菜单本身的颜色和形状。

欲了解如何在代码中实现这些行为的相关信息,请参阅Copy, Cut, and Paste Operations.

为了确保编辑菜单在你的应用中的表现符合用户期望,你应该:

显示在当前情境下合理的命令。例如,当没有对象被选择的时候,菜单中不应该包括复制或剪切(命令),因为这些命令是针对选择(的内容)而操作的。相似地,如果有对象被选择的时候,菜单中不应该包括选择(命令)。如果你在自定义页面中支持编辑菜单,你就有责任确保菜单中显示的命令切合当前的情境。

依据你的页面布局调节菜单显示。iOS依据可获得的空间大小选择在插入点的上方或下方来放置菜单指针以显示编辑菜单,这样用户就能看到菜单命令是如何与内容相关的。在必要的情况下,你可以通过程序在菜单显示之前决定它的位置,这样可以避免用户界面中的重要信息被遮挡。

支持两种手势来调用菜单。虽然点击和长按手势是用户呼起编辑菜单的首选方式,但他们也可以在文本页面中通过双击一个单词来选择该单词并同时呼起菜单。如果你在自定义页面中支持菜单,确保它能支持两种手势。除此之外,你可以定义用户双击时默认的选择对象。

避免在你的用户界面中创建和编辑菜单中功能相同的按钮。例如,使用编辑菜单让用户进行复制操作远比提供一个复制按钮要好,因为用户将会想知道为什么在你的应用中会有两种方法做同样的事。

如果静态文本的选择对用户来说是有用的,那么可以考虑使用它。用户可能想要复制图片的标题,但他们不可能想复制选项卡的标签或是屏幕的标题,比如“账户(Account)”。在文本页面内,文字的选择应该是默认设置的。

不要使按钮标题可选择。如果按钮的标题是可选择的,用户很难在不激活按钮的情况下呼出编辑菜单。通常来说,像按钮这样操作的元素不需要是可选择的。

将对撤销与重做的支持与对复制与粘贴的支持组合到一起。人们经常希望在他们改变主意的时候能撤销最近的操作。由于编辑菜单在它操作执行的时候是不需要确认的,你应该给用户提供撤销或重做这些操作的机会。

如果你需要创建自定义的编辑菜单项,需要像下面展示的这个例子一样遵循这些指导原则:

创建直接作用于用户选择的包含编辑、修改或其他操作的编辑菜单。人们期望在当前的情境内用标准的编辑菜单项操作文本或对象,那么你的自定义菜单项最好能有相似的表现。

将自定义项列在所有系统提供的项的后面。不要将你的自定义项与系统提供的项混置在一起。

保持自定义菜单项的数量在合理的范围内。你不应该用太多选择迷惑你的用户。

使用简洁的名称命名你的自定义菜单项并确保名称能准确的描述命令的作用。通常,项的名字应该是一个可以描述行为如何执行的动词。虽然你通常会使用单个的大写单词作为名字,但如果你必须使用一个短语(作为名字)时,就应使用标题式大写短语。(简洁的、标题性的大写词就是将除了文章、四字及四字以下的并列连词与介词之外的单词都大写。)

3.23 撤销与重做(Undo and Redo)

用户通过摇晃设备触发撤销操作,显示提醒框让他们可以:

  • 撤销他们刚才输入的内容
  • 重做先前撤销的输入
  • 取消撤销操作

你可以通过在你的应用中定义出更通用的方式来支持撤销操作:

  • 允许用户撤销或重做的行为
  • 在你的应用的哪种情形下晃动手势是用于撤销操作的
  • 支持多少步的撤销

欲了解如何用代码实现这一行为,请参阅Undo Architecture.如果在你的应用中支持撤销和重做,请遵循以下准则以提供好的用户体验:

为用户提供简洁的描述性短语使其能准确的获知他们正在撤销或重做的内容。iOS系统自动提供了“撤销”和“重做”的字符串(包括词语后面的空格)作为撤销警示按钮的标题,但你需要提供一或两个词语用于辅助描述用户的重做或撤销操作。例如,你可能提供文本的“命名”或“地址更改”之类的词语用以创建像“撤销命名”或“重新更改地址”这样的按钮标题。(要注意,在提醒框中,“取消”按钮是不能改变或移除的)。

避免提供的文本过长。太长的按钮标题容易被断章取义并且很难被用户解读。由于这个文本用于按钮的标题中的,要使用标题样式的大写形式并且不能添加标点。

避免过度使用摇晃手势。即使你能程式化地设定你的应用将摇晃事件作为用户激活撤销操作的途径,你也在冒着混淆用户视听的风险,因为他们也可能使用摇晃执行另一个不同的操作。分析你应用中的人机交互以避免创建那些用户无法可靠地预测摇晃手势结果的场景。

如果撤销和重做在你的应用中是基础性的任务,尽量使用系统原生的撤销与重做按钮。记住摇晃手势是用户触发撤销与重做操作的主要方式,而如果提供两种不同方式完成同样的任务则会使用户困惑。如果你认为很有必要提供直观专有的撤销与重做操作,你可以在导航栏中放置系统原生的按钮。(欲了解更多关于这些按钮的信息,参见Toolbar and Navigation Bar Buttons).

将撤销与重做能力与用户当下的情境进行清晰地关联,而非过早地介入情境。仔细考虑你允许进行撤销与重做操作的情境。通常来说,用户期望他们的改变和操作可以立即被有效的执行。

3.24 键盘和输入页面(Keyboards and Input Views)

在iOS8与之后的系统中,你可以创建自定义的键盘扩展内容来替代系统的原生键盘。欲了解更多关于管理应用内扩展(包括键盘)的信息,请参阅APP Extensions;欲了解如何开发自定义的键盘扩展内容的信息,请参阅Custom Keyboard.

在合适的情况下,你9也可以在你的应用内设计自定义的输入页面来替代系统原生的屏幕键盘。例如,Numbers(译者注:iWork中的电子表单应用程序)中提供了多种输入页面,这些页面设计使数量、日期和其他值的输入能简单高效地完成。

如果你提供自定义输入页面,确保它的功能对于来用户来说是清晰易懂的。

你也可以提供自定义的输入辅助视图,这种视图通常表现为显示在键盘(或你的自定义输入页面)上方的一个独立元素。例如,在某些情境中,Numbers会显示一个输入辅助视图用以帮助用户执行针对电子表格中的值的标准或自定义计算。

当用户在你的输入页面中敲击自定义控件时,使用标准的键盘敲击声提供声音反馈。欲了解在代码中如何使用这一声音,请参阅UIDevice Class Reference中的playInputClick章节

注意:标准的敲击音效只适用于当前屏幕上的自定义输入页面。人们可以在设置-声音中关闭所有的键盘音效(包括你的自定义输入页面中的那些)。

本章英文原文访问地址:iOS Human Interface Guidelines
本章中文翻译PDF下载:点此下载

腾讯ISUX

巧用不对称设计打造有趣的网站页面(如何在网站设计中运用围格系统)
« 上一篇 2024-09-15 16:18
【滴滴体验设计研究·实践篇】从 iPhone X 适配说起
下一篇 » 2024-09-15 16:18