iOS中的微交互。 Yandex讲座

几周前,在Yandex办公室举行了一个CocoaHeads社区的特别活动-比传统的mitaps大。 开发人员Anton Sergeev在这次会议上发表了讲话,并讨论了UX设计人员通常使用的微交互模型,以及如何将这些想法付诸实践。 安东最重视动画。


-对我来说很重要,能见到客人是我的荣幸。 我在这里看到与我认识很久的人,与我最近认识的人以及尚未与我见面的人。 欢迎来到CocoaHeads。

我会告诉您有关微交互的信息。 这有点棘手-我们是工程师,开发人员,我们将详细讨论软件部分,但我们将从一个非常人道的话题开始,例如微交互。 因此,我们将在技术部分中应用这一人道主义主题,以学习如何更有效,更简单地设计非常小的视觉组件,例如按钮,小型装载机,横杆。 它们充满了动画,分支的动画代码通常看起来很复杂,很难维护。

但是首先,要分心一些。 想想看,您还记得当您决定成为一名开发人员吗? 我清楚地记得这一点。 一切都始于一张桌子。 一旦我决定学习ObjC。 时髦的语言,有趣的,就这样,没有深远的计划。 我发现了一本书,似乎是《大书呆子牧场》,开始逐章阅读,做完每个练习,检查和阅读,直到到达桌子为止。 然后,我首先熟悉了代理模式,更确切地了解了它的亚种“数据源”,数据源。 现在,这种范例对我来说似乎非常简单:有一个数据源,委托,一切都很简单。 但这让我大吃一惊:如何将表格与完全不同的数据分开? 您曾经在一张纸上看到一张桌子,您可以在其中放置无数行,完全是抽象的数据。 它对我影响很大。 我意识到编程,开发有巨大的机会,应用它们将非常有趣。 从那时起,我决定成为一名开发人员。

在开发过程中,遇到了各种模式。 巨大,称为架构,描述了整个应用程序。 小巧的小巧小巧的按钮,可容纳数十个小按钮。 重要的是要了解所有这些模式都不是来自空中,而是来自人道主义部门。 相同的委托模式。 代表团早在编程之前就已出现,而编程接管了所有这些人道主义事务,以提高工作效率。

今天,我将讨论另一种接管另一项人道主义事务的方法。 特别是关于微相互作用。

一切始于装载机。 在Yandex之前的以前的工作中,我的任务是重复Google物料设计加载程序。 其中有两个,一个是不确定的,另一个是定义的。 我的任务是将它们组合为一个,他必须具有确定性和不确定性,但是有严格的要求-因此它非常平滑。 在任何时候,我们都可以从一种状态转移到另一种状态,并且所有内容都应该流畅且准确地动画化。

我是一个聪明的开发人员,我做了一切。 我收到了1000多行难以理解的面条代码。 它起作用了,但是我收到了有关代码审查的简短评论:“我真的希望没有人会编辑此代码。” 对我来说,这实际上是不合适的。 我写了糟糕的代码。 它很酷,是我最好的动画之一,但是代码很糟糕。

今天,我将尝试描述我辞职后发现的方法。



让我们从最人道的话题开始-微交互模型。 它们是如何嵌入的,并且通常隐藏在我们的应用程序中的什么位置? 让我们继续在技术世界中使用此模型。 考虑一下处理显示和动画的UIView如何工作。 特别是,我们将大量讨论CAAction机制,该机制与UIView,CALayer紧密集成并一起使用。 然后考虑一些小例子。

首先定义。 显然,作者真的很喜欢前缀“ micro”,但是没有宏观或纳米相互作用,大小无关紧要。 为了简单起见,我们将它们简称为交互。 这是一个非常方便的模型,可让您从头到尾描述与应用程序的任何交互。 它由四点组成:触发器,在此交互中需要实现的业务逻辑,将某些内容传达给用户的反馈以及应用程序状态的更改。

我将讲三个不同角色的故事。 我将从用户开始,这是开发中最重要的事情。 在准备报告时,我病了。 我需要找到一家药房,然后打开Yandex.Map。 我打开了应用程序,看着它,它看着我,但是什么也没有发生。 然后,我意识到自己是用户,我是主要用户,我给出了如何对应用程序进行操作的说明。 我进行了定向,单击“搜索”按钮,输入“药房”,单击“确定”,应用程序进行了内部工作,找到了我旁边必要的药房,并将其显示在屏幕上。

我搜索了正确的一个,发现除了药房外,屏幕上还出现了一个特殊的按钮-建立路线。 因此,应用程序已移至新状态。 我单击它,然后去了药房。 由于某种原因,我进入了此应用程序-寻找药房。 我到达了她。 我是一个快乐的用户。

在此应用程序出现之前,我能够在其中找到某些东西,它是首先开发的。 用户体验设计师在想出这个过程时会怎么想? 一切始于这样一个事实,即当用户和应用程序互相看时,必须退出静音场景,并且什么也没有发生。 为此,需要某种触发。 一切都有一个开始,在这里,也必须从某个地方开始。

选择了触发器-搜索按钮。 单击它时,有必要从技术角度解决问题。 在服务器上请求数据,解析响应,以某种方式更新模型,进行分析。 请求用户的当前位置等等。 这样我们就得到了这些数据,我们确切地知道了所有药房在哪里。

看来有可能结束这一点。 毕竟,我们解决了问题,找到了所有药房。 只有一个问题:用户仍然对这些药房一无所知。 他需要传达出来。

以某种方式将我们的解决方案打包起来,并以精美的包装将其带给他,以便他理解。 碰巧用户是人,他们通过感官与外界互动。 当前的技术状况使得我们作为移动应用程序的开发人员只有三种感觉:视觉-我们可以在屏幕上显示某些东西,听觉-我们可以在扬声器中再现声音,触觉可以将用户推到手。

但是人的功能要强大得多。 但是目前的技术状况使得我们目前只能依靠这三个方面。 在这种情况下,我们选择一个屏幕,在地图上显示最近的药房,并列出有关这些药房的更多详细信息。 看来这就是一切,用户找到了药房,一切都很好。

但是有一个问题。 当用户输入应用程序时,他所处的上下文中他不知道药房在哪里。 他的任务是找到她。 但是现在情况发生了变化,他知道药房在哪里,他不再需要寻找它们。 他有以下任务-获取前往下一个药房的路线。 这就是为什么我们需要在屏幕上显示其他控件的原因,特别是,这是用于构建路线的按钮,也就是说,将应用程序转移到另一个状态,在该状态下,该应用程序已准备好再次接受下一次交互的新触发器。

想象一下,UX设计人员想到了所有这些,然后来到开发人员手中,开始用彩色描述用户如何单击按钮,如何和发生什么,如何搜索,用户如何满意,如何增加DAU等等。 当我们第一次提到该按钮时,开发人员未解决的问题堆栈在第一句话的其他位置溢出。

他耐心地听一切,最后,当一切结束时,他说很好,这很酷,但让我们讨论一下按钮。 这是重要的元素。

在讨论中,事实证明该按钮本质上是一个触发器,它内部包含逻辑,通过它可以接收来自系统的消息,特别是有关用户在屏幕上单击的消息。 基于此单击,它可以启动一系列事件,首先是相同的按钮将有关需要启动各种进程的消息发送到不同的对象,在这种情况下,该消息要求在服务器上请求更多信息。

按下时,按钮将更改其状态,然后变为按下状态。 当用户释放时,它不再被按下。 即,它向用户提供反馈,以便他了解该按钮的期望。 并且按钮可以在不同的状态下被按下,未按下,活动或不活动,并根据不同的逻辑从一种状态移动到另一种状态。

因此,我们看到,由触发器,业务逻辑,反馈和状态变化组成的同一微交互模型可以在各种规模上描述我们的应用程序,例如在整个用户案例的规模上,对最近的药房的巨大搜索,所以就一个小按钮而言。

这是一个非常方便的模型,可让您简化团队内部的交互并以编程方式描述四个独立的实体:触发器,业务逻辑,反馈和状态更改。 让我们看看UIKit为我们提供了使用它的方式。 不仅提供,而且使用。 在实现各种动画时,UIView子类的小组件仅使用此机制,而不会采用其他方式。

让我们从UIView开始,它如何适合这个模型。 然后,我们将考虑它为我们提供的支持这些状态的CALayer,并且我们将考虑行动机制,这是最有趣的时刻。

让我们从UIView开始。 我们用它在屏幕上显示一些矩形。 但是实际上,UIView不知道如何绘制自身,为此它使用了另一个CALayer对象。 实际上,UIView致力于接收有关触摸系统以及其他调用的消息,这些消息涉及我们在UIView子类中定义的API。 因此,UIView本身实现了触发逻辑,即启动某些进程,从系统接收这些消息。

UIView还可以将发生的事件通知给其委托人,还可以将消息发送给订阅者,例如,使UIControl子类具有不同的事件。 这样,就可以实现此UIView的业务逻辑。 并非所有人都具有业务逻辑,其中许多只是显示元素,没有业务逻辑意义上的反馈。



我们看了两点,触发器和业务逻辑。 UIView中的反馈和状态更改隐藏在哪里? 要理解这一点,我们必须记住UIView本身并不存在。 创建后,它将创建一个背层,即CALayer的子类。



并任命自己为代表。 要了解UIView如何使用CALayer,它可以以各种状态存在。

如何将一种状态与另一种状态区分开? 它们的区别在于需要存储在某处的数据集。 我们将考虑CALayer为UIView提供哪些功能,以便它存储状态。



我们的界面正在扩展,UIView和CALayer之间的交互,UIView还有一个附加任务-更新CALayer内部的存储库。

鲜为人知的鲜为人知的事实:CALayer可以表现为关联数组,这意味着我们可以在任意键上向其写入任意数据,如下所示:setValue(_:forKey :)。



此方法存在于所有NSObject子类中,但是与许多其他子类不同,当接收到一个未被其覆盖的键时,它不会崩溃。 然后他正确地写下来,然后我们就可以阅读了。 这是非常方便的事情,无需创建CALayer的子类,就可以在其中写入任何数据,然后读取它们并进行查询。 但这是一个非常原始的简单存储库,实际上是一个字典。 CALayer更加进步。 它支持样式。

这是由任何CALayer拥有的Style属性实现的。 默认情况下,它是nil,但是我们可以重新定义和使用它。



通常,这是一本常规词典,仅此而已,但如果我们要求提供NSObject的另一种方法forKey的值,它对CALayer的工作方式就具有特殊性。 它的作用非常有趣,它在样式字典中递归搜索必要的值。 如果我们使用样式键将一个现有样式打包为新样式并在其中编写一些键,但是它将以以下方式显示。



首先,查看根部,然后查看内陆等,直到有意义为止。 当样式变为零时,往远处看毫无意义。

这样,UIView可以使用CALayer提供的基础结构,组织状态更改,使用样式(可以模拟堆栈的功能非常强大的存储库)或使用常规关联数组来更新内部CALayer存储库,这也非常高效且非常有用。

完成存储后,从CAAction开始。 我会告诉你更多关于他的事。



UIView有一项新任务-向CALayer请求操作。 什么是动作?



CAAction只是一种只有一种方法的协议-运行。 苹果通常喜欢电影主题,在这里的动作就像“照相机,马达!”。 该“马达”仅是一个动作,而不仅仅是使用该名称。 run方法意味着启动一个可以开始,执行和结束的动作,这是最重要的。 此方法非常通用,只有一个事件字符串,其他所有类型都可以。 在ObjC中,这都是id和正常的NSDictionary。



UIKit中有满足CAAction协议的类。 首先是动画。 首先,我们知道可以将动画添加到图层中,但这是非常底层的事情。 在其之上的高级抽象是使用带有必要参数的动作运行一个动作。

第二个重要例外是NSNull。 我们知道不能用任何方法调用他,但是他满足CAAction协议,这样做是为了方便地在层中搜索CAAction。



如前所述,UIView是CALayer的委托,并且委托方法之一是action(用于:forKey :)。 该层有一个方法,动作为key。



我们可以在层上的任何时候调用它,并且在任何时候它都会给出正确的动作或nil,因为它也可以给出。 该算法是非常不寻常的搜索。 伪代码写在这里,让我们看一下代码行。 收到此消息后,他首先与代表协商。 委托可以返回nil,这意味着搜索应在其他位置继续进行,或者可以返回有效的操作,即满足CAAction协议的有效对象。 但是有一个逻辑规则:如果它返回满足此协议的NSNull,则它将稍后转换为nil。 也就是说,如果我们返回Null,则实际上意味着“停止搜索”。 没有任何动作,没有必要。

但是有以下内容。 在他与代表协商之后,代表返回零,他继续寻找。 首先,在该图层具有的Actions字典中,然后它将在样式字典中进行递归搜索,在该字典中还可以有一个带有action键的字典,可以在其中写入许多动作,并且还可以递归搜索它们。 如果仍然无法解决问题,他将要求该类提供默认操作forKey方法,该方法由CALayer定义,直​​到最近返回了一些内容,但最近在最新版本的iOS中它始终返回nil。

理解了理论。 让我们看看如何在实践中应用一切。

有事件,有键,在这些事件上会发生一些动作。 从根本上讲,可以区分两种不同类型的事件。 第一个是存储属性的动画。 假设当我们在View上调用Viewcolor = red时,理论上可以进行动画处理。



关于无电路模式的报告如何? 我画了几张。 UIView具有我们为子类定义的某种接口,或者是从系统中接收到的带有事件的接口。 UIView的任务是请求所需的操作,更新内部存储并启动发生的操作。 对于请求而言,顺序非常重要:操作,然后才更新操作,然后才更新存储和操作。



如果在UIView上更新backgroundColor会发生什么。 我们知道,在UIView中,与屏幕显示相关的所有内容仍然是CALayer的代理。 为了以防万一,他会缓存收到的所有内容,但同时所有内容都会广播CALayer,并且他会在CALayer上进一步处理所有逻辑。 CALayer收到更改背景的任务时会发生什么? 这里的一切都比较复杂。



对于初学者,他将要求采取行动。 而且重要的是要了解将首先请求该操作。 这将允许您在创建操作时询问CALayer的当前值,包括backgroundColor,直到存储被更新,并且当接收到的操作接收到run命令时,它才能查询CALayer并获取新值。 因此,他将同时拥有新旧事物,这将使他能够根据需要创建动画。

但是UIView中有一个功能,如果我们更改UIView中的backgroundColor,如果在动画块中执行此操作,则会对其进行动画处理,如果它在动画块之外,则不会进行动画处理。



一切都很简单,没有魔术。 但是请记住,UIView是CALayer的委托,它具有这种方法。 一切都非常简单。

如果在动画块中运行了此方法,则它将返回某种动作。 如果在动画块之外,则此方法将返回NSNull,这意味着不需要动画。 , CALayer .

, UIView , . . ?



, . UIView , read only, , inheritedAnimationDuration. . , . .

? duration, . , run, , .



, CAAction, backgroundcolor opacity, UIView . , , , , . . setValue forKey , , , , , , .

, , , , .

— . , «» «» . .

.



. , , , . UIView CALayer, , , CAAction, , , .





, , , . , . .

. - .

CAAction, , . , , , .



, , , home, . , . , .



. - .



, - , - . , , . , .

, , .



, CAAction , . , UIControl, - , - , , , - .

, . , UIView -, , - , , , .

— . .

? . , . — . activating, inactive active. , , .



. , onOrderIn onOrderOut. , UIKit, .

, -, — , .



. UIView , : isActive progress. . CAAction, .

, . , , 30 CAACtion, . , 30 , NSNull. 15 15 . . — . , — .

, . .

. , , : , -, .

通过将所有交互分解为这四个部分,可以简化彼此之间的逻辑。因此,尝试使用这些微交互来分析您的应用程序和各种任务。请记住,UIKit提供了一个庞大的基础架构来方便,美观地完成此任务。不要忽视她。通常有非常古老的方法,很少使用,但它们非常重要,可帮助您轻松,精美,轻松地实现元素。谢谢您的关注。

Source: https://habr.com/ru/post/zh-CN429580/


All Articles