如何在SwiftUI App中实现类似Duolingo的可点击文本片段弹出Popover
在SwiftUI中实现可点击文本弹出Popover(类似Duolingo效果)
方法一:拼接Text组件实现简单交互
这种方式适合短文本、少量可点击片段的场景,直接拆分可点击文本,绑定点击手势与Popover。
import SwiftUI struct ContentView: View { @State private var showPopover = false var body: some View { VStack(spacing: 20) { Text("你已经完成了") + Text("5个关卡") .foregroundColor(.blue) .onTapGesture { showPopover = true } + Text(",继续加油!") } .popover(isPresented: $showPopover) { // 自定义Popover内容 VStack(spacing: 16) { Text("关卡详情") .font(.headline) Text("你已解锁基础词汇模块全部关卡,可挑战进阶内容!") .font(.body) .padding() } .padding() .frame(width: 250) } } }
方法二:基于UITextView的富文本交互
如果需要处理长文本或多个可点击片段,可通过UIViewRepresentable包装UITextView,利用富文本链接触发Popover。
import SwiftUI import UIKit struct ClickableTextView: UIViewRepresentable { let attributedText: NSAttributedString @Binding var showPopover: Bool func makeUIView(context: Context) -> UITextView { let textView = UITextView() textView.attributedText = attributedText textView.isEditable = false textView.isSelectable = true textView.delegate = context.coordinator textView.backgroundColor = .clear return textView } func updateUIView(_ uiView: UITextView, context: Context) { uiView.attributedText = attributedText } func makeCoordinator() -> Coordinator { Coordinator(parent: self) } class Coordinator: NSObject, UITextViewDelegate { let parent: ClickableTextView init(parent: ClickableTextView) { self.parent = parent } func textView(_ textView: UITextView, shouldInteractWith URL: URL, in characterRange: NSRange, interaction: UITextItemInteraction) -> Bool { if URL.scheme == "popover" { parent.showPopover = true return false } return true } } } struct ContentView: View { @State private var showPopover = false var body: some View { VStack { ClickableTextView(attributedText: createAttributedText(), showPopover: $showPopover) .frame(height: 50) } .popover(isPresented: $showPopover) { VStack(spacing: 16) { Text("关卡详情") .font(.headline) Text("你已解锁基础词汇模块全部关卡,可挑战进阶内容!") .font(.body) .padding() } .padding() .frame(width: 250) } } private func createAttributedText() -> NSAttributedString { let string = "你已经完成了5个关卡,继续加油!" let attributedString = NSMutableAttributedString(string: string) if let range = string.range(of: "5个关卡") { let nsRange = NSRange(range, in: string) attributedString.addAttribute(.link, value: "popover://levelDetail", range: nsRange) attributedString.addAttribute(.foregroundColor, value: UIColor.blue, range: nsRange) attributedString.addAttribute(.underlineStyle, value: NSUnderlineStyle.single.rawValue, range: nsRange) } return attributedString } }
注意事项
- 方法一中,拼接的Text组件需保持字体、行高一致,避免排版错乱。
- 方法二中,可修改UITextView的字体、行间距等属性,匹配SwiftUI原生Text风格。
- Popover内容可完全自定义,支持添加图片、按钮等交互元素。
内容的提问来源于stack exchange,提问作者sanket patel
相关产品推荐
相关产品推荐

