You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何在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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.06.27 21:23:35