如何用SwiftUI实现类似Duolingo的选词填空横线效果?
SwiftUI单词排序组件改造:实现Duolingo式横线布局
需求说明
当前已实现单词点击移入指定区域的功能,需将原白色大容器替换为独立横线布局,让选中单词放置在横线上,效果匹配Duolingo的单词排序样式。
改造后代码(单行版本,适配长句子横向滚动)
ScrollView(.horizontal, showsIndicators: false) { HStack(spacing: 12) { ForEach(arrangedWords.indices, id: \.self) { index in let targetWord = arrangedWords[index] VStack(spacing: 4) { // 有单词时显示卡片 if !targetWord.isEmpty { Text(targetWord) .foregroundColor(.white) .padding(.horizontal, 16) .padding(.vertical, 8) .background(Color.green.opacity(0.8)) .cornerRadius(8) .animation(.spring(), value: arrangedWords) .onTapGesture { moveWordOutOfSpace(index: index) print("arranged words: \(arrangedWords)") } } // 始终显示横线占位 Rectangle() .fill(Color.gray) .frame(height: 3) .frame(minWidth: 60) // 可根据单词长度调整横线最小宽度 } } } .padding(.vertical, 16) .padding(.horizontal, 10) }
改造后代码(多行版本,保留原布局逻辑)
ScrollView { VStack(alignment: .leading, spacing: 16) { ForEach(0..<(arrangedWords.count + itemsPerRow - 1) / itemsPerRow, id: \.self) { row in HStack(spacing: 12) { ForEach(0..<itemsPerRow, id: \.self) { wordIdx in let index = row * itemsPerRow + wordIdx if index < arrangedWords.count { let targetWord = arrangedWords[index] VStack(spacing: 4) { if !targetWord.isEmpty { Text(targetWord) .foregroundColor(.white) .padding(.horizontal, 16) .padding(.vertical, 8) .background(Color.green.opacity(0.8)) .cornerRadius(8) .animation(.spring(), value: arrangedWords) .onTapGesture { moveWordOutOfSpace(index: index) print("arranged words: \(arrangedWords)") } } Rectangle() .fill(Color.gray) .frame(height: 3) .frame(minWidth: 60) } } } } } } .padding(.vertical, 10) .padding(.horizontal, 10) }
关键改动说明
- 移除大容器样式:删掉原代码中
background(.white)、cornerRadius(10)及边框overlay,改用每个位置独立的横线作为占位 - 布局结构调整:用
VStack将单词卡片(非空时显示)和横线垂直排列,实现单词悬浮在横线上的效果 - 视觉细节优化:调整单词卡片的内边距、圆角,贴近Duolingo的设计风格;给横线设置最小宽度,避免空位置横线过窄
- 滚动逻辑适配:单行版本使用横向
ScrollView,适合长句子排版;多行版本保留原有的行拆分逻辑,适配多单词场景
内容的提问来源于stack exchange,提问作者Iku
相关产品推荐
相关产品推荐

