行业

汽车

航空

金融

游戏

医疗科技

物联网

数字产品

使用场景

面向企业

面向个人

面向团队

用于教育

主要特点

硬件与系统集成

ProtoPie MCP

多设备原型设计

交互逻辑

开发交接

ProtoPie AI

新

用户测试

插件

Figma

Sketch

Adobe XD

洞见与灵感

博客

白皮书

客户成功案例

活动与网络研讨会

ProtoPie 画廊

产品学习与支持

ProtoPie 学堂

下载 ProtoPie

产品文档

社区

行业

汽车

航空

金融

游戏

医疗科技

物联网

数字产品

使用场景

面向企业

面向个人

面向团队

用于教育

主要特点

硬件与系统集成

ProtoPie MCP

多设备原型设计

交互逻辑

开发交接

ProtoPie AI

新

用户测试

插件

Figma

Sketch

Adobe XD

洞见与灵感

博客

白皮书

客户成功案例

活动与网络研讨会

ProtoPie 画廊

产品学习与支持

ProtoPie 学堂

下载 ProtoPie

产品文档

社区

教程
•

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() 会在一段文本中查找另一段文本是否存在。它有两个参数:source 和 searchValue。

  • 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() 函数时,大小写是敏感的。对它来说,C 和 c 不是同一个字符。但如果接受这种行为,体验会很差。

要解决这个问题,我们需要去掉大小写敏感。做法是:在执行 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:1,Song 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 中,每个副本都会将自己的 id 与 hiding_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 开始,跟随搜索栏教程打造流畅的用户体验。

免费开始

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

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