教程

15

阅读时间

如何制作搜索栏原型

使用 ProtoPie 的“indexOf”和“lowerCase”函数制作一个功能完整的搜索字段/搜索栏原型。

杰夫·克拉克, 用户体验设计师与 ProtoPie 培训师

快速总结

几乎每一种数字化体验都会包含某种搜索体验,而且你很可能在某个时候需要为这种体验制作原型。可用的搜索栏通常超出了大多数原型工具的能力范围,因此作为设计师,我们往往只能通过展示几张截图来“伪造”这种体验,展示搜索在几个典型状态下的样子。

💡 在 ProtoPie 里,这个限制不存在!

ProtoPie 强大的免代码逻辑模型,让你能够快速且轻松地做出真正可用的搜索栏体验。此外,很多原型工具虽然支持创建组件来复用视觉样式,但 ProtoPie 更进一步:它还允许你复用交互,以及(对本教程更重要的)逻辑。

看看下面的演示,了解你能做出什么。


prototype that includes a functioning search bar


搜索栏原型

起始 Pie 文件完成版 Pie 文件

概览

  • 你将学到什么

  • 开始之前

  • 分步指南

  • 第 1 部分:设置搜索

  • 第 2 部分:在搜索栏中显示结果

  • 第 3 部分:让搜索不区分大小写

  • 第 4 部分:折叠结果

  • 为什么这样可行?

  • 就是这样!像 Pie 一样简单!

  • 用完整可用的搜索栏提升你的原型

你将学到什么

在本教程结束时,你将学会

  • 如何使用内置的 indexOf()lowerCase() 函数。

  • 如何让组件与场景以及组件之间相互通信。

  • 如何把可复用逻辑构建进组件中

  • 构建一个可用搜索体验有多快多简单。我说真的! 你会惊讶于这有多容易!

完成时间:≤15 分钟

开始之前

首先,打开起始 Pie 文件。它是一个模拟音乐应用,包含 8 首歌曲列表。列表顶部是搜索框。列表中的每首歌都是一个名为“Song”的组件副本。之所以做成组件,是因为它们的结构完全一致。

我们没有把同样的工作重复 8 次,而是用组件先创建一次结构,然后在场景中使用 8 个副本,并分别自定义封面图、歌曲标题和歌手。

我们会在场景中以及 Song 组件的主副本中同时进行操作,让搜索栏工作起来。

分步指南

第 1 部分:设置搜索

先从在主场景中为搜索栏添加一些交互开始。

  • Search field 输入层上添加一个 Detect Trigger。将要检测的属性选择为 Text


Add a Detect Trigger to the Search Field input layer


Detect Trigger

  • 添加一个 Send Response。Channel 选择“Send to Current Scene”。消息使用 SEARCH,并勾选“Send Value Together”。在出现的输入框中使用以下公式:``Search field.text


Add a Send response


Send Response

💡 这里发生了什么?

每当搜索框的 text 属性值发生变化时,Detect Trigger 就会触发。每次按键时,消息 SEARCH 都会连同搜索框内容一起发送到场景。任何配置为监听该消息的对象都会收到并作出响应。我们接下来就做这件事!

把歌曲列表中的每一项做成组件的好处是,我们可以把逻辑放在主组件内部,这样逻辑会包含在所有副本中。这样一来,每个组件都可以独立判断自己是否匹配搜索。

  • 切换到 Song 组件的主副本。


Go to the Component Scene


Component Scene

现在我们正在编辑 Song 组件的主副本,在这里做的任何修改都会自动同步到主场景中的所有副本。接下来让组件对 SEARCH 消息作出反应。

  • 添加一个 Receive Trigger。channel 选择“Receive from Current Scene”。消息使用 SEARCH。勾选“Assign to Variable”。组件里目前还没有变量,所以我们需要先创建一个。


Add a Receive trigger


Receive Trigger

💡 配置 Receive Trigger 时,消息内容以及接收通道都必须与对应的 Send Response 完全匹配。

  • 在组件中创建一个变量,命名为 searchKeyword。确保类型设为 Text


Create a variable called searchKeyword


创建一个名为 searchKeyword 的变量

  • 现在回到上一步创建的 Receive Trigger,在“Assign to Variable”下选择新建的 searchKeyword 变量。


Choose the newly created variable under assign to variable


在 assign to variable 下选择新建变量

💡 每当 Pie 收到一个附带值的消息时,在使用这个值之前,你需要先把它赋给一个变量。这就是我们选择 Assign to Variable 时正在做的事。

第 2 部分:在搜索栏中显示结果

我们已经设置好 Song 组件从场景接收搜索关键词。现在我们希望判断该关键词是否匹配歌曲标题的任意部分。

我们将在条件中使用 ProtoPie 内置的 indexOf() 函数。indexOf() 会在一段文本中查找另一段文本是否存在。它有两个参数:sourcesearchValue

  • source 是你要搜索的文本。在这里是歌曲标题。

  • searchValue 是你要在 source 中查找的关键词。这将是我们传入变量 searchValue 的值。

当我们执行 indexOf() 时,它会返回一个数字,表示找到的文本位置;如果未找到则返回 -1。我们关注两种情况:

  • 如果函数返回 -1,说明歌曲不匹配搜索,因此应从结果中排除。

  • 如果函数返回的值不是 -1,那么这首歌匹配搜索,应包含在结果中。

来看看 indexOf() 的实际效果!

  • 仍然在 Song 组件主副本中,在 Receive Trigger 下添加一个 condition。

  • 在这个 condition 中,我们先检查第一种情况——未匹配。

  • 第一个下拉框选择“Formula”。

  • 使用以下公式:indexOf('``Title'``.text, searchKeyword),然后点击 OK。

  • 运算符保持 =(等于),底部输入框填写 -1


Use ProtoPie’s built-in indexOf() function in our condition


在 condition 中使用 ProtoPie 内置的 indexOf() 函数

💡 Operator 用于告诉 ProtoPie 你想要如何进行比较。看看 condition 中间那排图标——> ≥ < ≤ = ≠——这就是 ProtoPie 提供的全部运算符。

  • 当没有匹配时,我们隐藏歌曲。在 condition 下添加一个 Opacity Response。选择 Song 层(组件的顶层容器)。opacity 设为 0,duration 设为 0。将 duration 设为 0 可确保透明度变化不带动画,立即生效。


Add an Opacity Response under our condition


在 condition 下添加 Opacity Response

现在我们还需要处理第二种情况,也就是有搜索匹配时。

  • 再添加一个 condition。上半部分使用相同公式:indexOf('``Title'``.text, searchKeyword)

  • 这次运算符选择 (不等于)。值同样填写 -1


Add another condition under the Receive trigger


在 Receive trigger 下再添加一个 condition

为了更清晰,我们来重命名这些 condition。

  • 双击第一个 condition,重命名为 NO MATCH

  • 双击第二个 condition,重命名为 MATCH


Let’s rename our conditions
  • 我们来重命名 condition*

到这一步,我们应该已经有一个可工作的搜索了。回到主场景。

  • 预览你的作品。输入时,你应该会看到不匹配搜索词的歌曲消失;按退格后又会重新出现。


Preview of search bar prototype in the preview window


在预览窗口中预览你的作品

仅用几个简单步骤,你就创建了可用搜索体验的基础!

确实能工作,但你可能注意到了一个问题。如果用大写“C”搜索,会有三首歌保持可见;但如果用小写“c”搜索,就没有结果。


Search bar prototype shows different search results for lowercase and uppercase letters


小写和大写字母得到不同搜索结果

我们来修复它!

第 3 部分:让搜索不区分大小写

使用 indexOf() 函数时,大小写是敏感的。对它来说,Cc 不是同一个字符。但如果接受这种行为,体验会很差。

要解决这个问题,我们需要去掉大小写敏感。做法是:在执行 indexOf() 前,把搜索关键词歌曲标题这两者都转换成小写。

ProtoPie 还有一个可用的内置函数:lowerCase()。顾名思义,它会把文本转成小写。例如,lowerCase("ProtoPie is the BEST!!") 会得到 protopie is the best!!

来看看 lowerCase() 的实际效果!

仍然在主场景中,我们来修改 Detect Trigger 下的 Send Response。

  • 在“Send Value Together”下的输入框中,将现有公式修改为使用 lowerCase()lowerCase('Search field'.text)。这样就能确保所有组件接收到的都是搜索栏输入内容的小写版本。


Modified existing formula to use the lowerCase() function


将现有公式修改为使用 lowerCase() 函数

💡 注意 lowerCase() 函数名中的大写“C”。函数名同样区分大小写。如果误写成小写“c”的 lowercase(),公式将无法运行。

在执行 indexOf() 前,我们也要对 Song标题做同样处理。

  • 再次编辑 Song 组件的主副本。

  • 我们需要修改两个 condition。点击第一个 condition,将公式改为:indexOf(lowerCase('Title'.text), searchKeyword)


first condition


修改第一个 condition

**💡 这里发生了什么? **

一个函数可以嵌套在另一个函数里,因此也可以作为参数传入另一个函数。

由于 indexOf() 的第一个参数需要 Text,我们可以使用任何输出 Text 的函数——而 lowerCase() 正是如此!像这样嵌套时,内部函数会先执行,外部函数后执行。最终 indexOf() 处理的就是已转为小写的歌曲标题。

  • 把第二个 condition 中的公式也做同样修改。


second condition


修改第二个 condition

回到主场景再次预览。现在你的搜索应该不区分大小写了。

第 4 部分:折叠结果

我们的搜索已经完全可用,但不匹配的歌曲隐藏后,会留下空白区域。我们希望在显示结果时把这些空白折叠起来。

就像我们可以从主场景向组件发送消息一样,组件之间也可以互相发送消息。不过在此之前,我们需要修改组件,让它们能够区分彼此。

  • 编辑 Song 组件的主副本。

  • 创建一个名为 id 的变量。保持默认 Number 类型。勾选“Make Overridable”。


variable called id


创建一个名为 id 的变量

💡 勾选“Make Overridable”后,场景中每个组件副本都可以拥有不同的 id 值。

  • 返回主场景。每个 Song 组件副本在右侧属性面板都有一个“Overrides”区域,你会看到新建的 id 变量可被编辑。


The Overrides section in the property panel


属性面板中的 Overrides 区域

  • 为每个组件副本设置不同的 id 值,使用连续编号。

  • 例如,第一项“The Celebrated Ways”设置为 1

  • 第二项“Feel Again”设置为 2

  • 第三项“Good Enemy”设置为 3

  • 以此类推。


Give each component a different value for id


为每个组件设置不同的 id 值

💡 **这种连续编号在我们的逻辑中会非常重要。**

幸运的是,各副本的图层名称与 id 应有的值相对应。花点时间再检查一遍:Song 1 对应 id:1Song 2 对应 id:2,一直到 Song 8

现在让魔法发生吧!

  • 再次编辑 Song 组件的主副本。

  • 在“NO MATCH” condition 下添加一个 Send Response。Channel 选择“Send to Current Scene”。消息使用 REORDER。勾选“Send Value Together”,并在下方输入框中使用公式 id


Send Response under the condition


在 condition 下添加 Send Response

  • 创建一个新变量,命名为 hiding_id。保持默认 Number 类型。


new variable called hiding_id


创建一个名为 hiding_id 的新变量

  • 在“NO MATCH” condition 下添加一个 Send Response。

  • 添加一个 Receive Trigger。Channel 选择“Receive from Current Scene”。消息使用 REORDER。勾选“Assign to Variable”。

  • 在 Receive Trigger 中,于“Assign to Variable”下选择新建的 hiding_id 变量。


hiding_id variable


选择 hiding_id 变量

继续之前,有必要说明一下将会发生什么。

我们在“NO MATCH” condition 中使用 Send Response 广播了 REORDER 消息。如果组件会说话,这个特定副本的 Song 组件相当于在告诉其他所有副本:“我的 id 是 x。我不匹配搜索,所以我要隐藏。其他组件请相应地重新排序。”

此时,Song 组件的所有副本都会收到该消息,包括发送消息的那个副本本身。我们还把发送者的 id 一并发送,并赋值给名为 hiding_id 的变量。在 Receive 中,每个副本都会将自己的 idhiding_id 进行比较。

关键点在这里:

如果接收者的 id 大于发送者的 id(即 id > hiding_id),我们就把接收者向上移动一个位置。这就是为什么给每首歌设置 id 时必须使用连续编号。

来看实际效果。

  • 在 Receive Response 下添加一个 condition。第一个下拉框选择变量 id。点击 >(大于)运算符。底部下拉框选择变量 hiding_id


Condition under the Receive Response


在 Receive Response 下添加 condition

  • 在该 condition 下,给 Song 层添加一个 Move Response。在“Position”下拉框中选择“Move By”,并在 Y 输入框中填入 -84。最后将 duration 设为 0,确保该移动无动画。


Added Move Response to the Song layer


在 condition 下为 Song 层添加 Move Response

这样就全部完成了!回到主场景并预览。你的搜索现在应该已经完整可用了!

为什么这样可行?

如果你对它为什么能工作还不太清楚,我们来回顾一下逻辑。如果上面的内容你都理解了,也可以直接跳过。

隐藏、重置与重排会在每一次按键时发生

每当在搜索栏输入一个字符,每个组件都会重新判断自己是否匹配搜索,不会记住之前是否匹配。如果匹配,它会重置自己(位置和透明度),即使它已经可见且处于原始位置;如果不匹配,它会隐藏自己,即使它已经隐藏。任何正在隐藏自己的项目都会广播 REORDER 消息并附带自身 id,而它下方的所有组件(例如 id 大于该隐藏组件的组件)都会向上移动 84 像素。

来看一个例子。我们只看前 3 首歌,忽略其余部分。

  1. “The Celebrated Ways”

  2. “Feel Again”

  3. “Good Enemy”

假设用户在搜索栏中输入字符 n。会发生以下情况:

  • Song 1 不匹配并隐藏自己。

  • Song 1 向所有组件广播 REORDER,并附带自己的 id 值。

  • Song 2 匹配并重置自己。

  • Song 2 收到来自 Song 1 的 REORDER,并上移到第一个位置。

  • Song 3 匹配并重置自己。

  • Song 3 收到来自 Song 1 的 REORDER,并上移到第二个位置。

现在假设用户继续输入,下一个字符是 e,搜索词变成 ne。会发生以下情况:

  • Song 1 不匹配并隐藏自己,即使它已经是隐藏状态。

  • Song 1 向所有组件广播 REORDER

  • Song 2 不匹配并隐藏自己。

  • Song 2 向所有组件广播 REORDER

  • Song 2 收到来自 Song 1 的 REORDER 并上移到第一个位置,但因为它已隐藏,我们看不到。

  • Song 3 匹配并重置自己,把它放回原始的第 3 位。

  • Song 3 收到来自 Song 1 的 REORDER,并上移到第二个位置。

  • Song 3 还会收到来自 Song 2 的 REORDER,并再次上移到第一个位置。

就是这样!像 Pie 一样简单!

在创建可用搜索体验的过程中,你成功使用了 ProtoPie 的许多强大功能:

  • 函数与公式

  • 你学会了如何使用 indexOf()lowerCase() 函数

  • 你学会了如何在公式中将它们互相嵌套,以执行复杂的信息处理

  • Send & Receive

  • 你使用 Send 和 Receive,让主场景能够与所有组件通信,也让组件之间能够彼此通信。

  • 功能型组件

  • 你的组件不仅在视觉样式上可复用,也在功能上可复用!事实上,Song 组件承担了实现该体验的大部分工作。

最重要的是,我希望这能让你看到:让它工作起来其实非常简单。只涉及几个内置函数,而且——最重要的是——无需代码!

用完整可用的搜索栏提升你的原型

准备好搜索、点击并惊艳他人了吗?那就从 ProtoPie 开始,跟随搜索栏教程打造流畅的用户体验。

免费开始

想了解更多原型设计内容?

[这篇内容有帮助吗?告诉我们!]