List interaction
This example demonstrates how to implement interactive list items in the Scripting app using swipe gestures. By leveraging leadingSwipeActions and trailingSwipeActions, you can provide contextual actions such as marking a message as unread, deleting a message, or flagging it.
Overview
You will learn how to:
- Display a list of messages using a custom cell layout
- Implement swipe actions on both leading and trailing edges
- Configure swipe behavior (e.g. disabling full swipe)
- Use
Button, Label, and Circle for interactive UI elements
Example Code
1. Define Message Data Type
1type Message = {
2 from: string
3 content: string
4 isUnread: boolean
5}
2. Create a Custom Message Cell
Each message is rendered with a colored indicator (for unread status), sender name, and content using HStack and VStack.
1function MessageCell({
2 message
3}: {
4 message: Message
5}) {
6 return <HStack>
7 <Circle
8 fill={message.isUnread ? "systemBlue" : "clear"}
9 frame={{
10 width: 16,
11 height: 16,
12 }}
13 />
14 <VStack alignment={"leading"}>
15 <Text font={"headline"}>{message.from}</Text>
16 <Text>{message.content}</Text>
17 </VStack>
18 </HStack>
19}
3. Manage State and Actions
1const [messages, setMessages] = useState<Message[]>(...)
2
3function toggleUnread(message: Message) {
4 setMessages(messages.map(item =>
5 item !== message ? item : { ...message, isUnread: !item.isUnread }
6 ))
7}
8
9function deleteMessage(message: Message) {
10 setMessages(messages.filter(item => item !== message))
11}
4. Construct the List with Swipe Actions
1return <NavigationStack>
2 <List
3 navigationTitle={"Messages"}
4 navigationBarTitleDisplayMode={"inline"}
5 listStyle={"inset"}
6 >
7 {messages.map(message =>
8 <MessageCell
9 message={message}
10 leadingSwipeActions={{
11 allowsFullSwipe: false,
12 actions: [
13 <Button
14 action={() => toggleUnread(message)}
15 tint={"systemBlue"}
16 >
17 {message.isUnread
18 ? <Label title={"Read"} systemImage={"envelope.open"} />
19 : <Label title={"Unread"} systemImage={"envelope.badge"} />
20 }
21 </Button>
22 ]
23 }}
24 trailingSwipeActions={{
25 actions: [
26 <Button
27 role={"destructive"}
28 action={() => deleteMessage(message)}
29 >
30 <Label title={"Delete"} systemImage={"trash"} />
31 </Button>,
32 <Button
33 action={() => {}}
34 tint={"systemOrange"}
35 >
36 <Label title={"Flag"} systemImage={"flag"} />
37 </Button>
38 ]
39 }}
40 />
41 )}
42 </List>
43</NavigationStack>
5. Present the View and Exit
1async function run() {
2 await Navigation.present({
3 element: <Example />
4 })
5
6 Script.exit()
7}
8
9run()
Key Features
- leadingSwipeActions: Add actions triggered by swiping from the leading edge (left-to-right in LTR layouts).
- trailingSwipeActions: Add actions triggered by swiping from the trailing edge.
- allowsFullSwipe: When set to
false, prevents full swipe from automatically triggering the first action.
- Button Roles: Use roles like
"destructive" to style buttons (e.g., red for delete).
- tint: Customize button color for better visual context.
Use Cases
- Email/Messaging Scripts: Mark messages as read/unread, delete, archive, or flag.
- To-Do Lists: Complete or remove tasks with quick gestures.
- Custom Tools: Attach context-specific actions to list items.
Swipe actions provide an efficient and intuitive way for users to perform actions directly within list views, improving interaction speed and user experience.