SwiftUI 可点击的潜台词

Kar*_*yan 16 xcode text ios swift swiftui

当点击文本的某些部分时,SwiftUI 中是否有任何方法可以打开浏览器。

我尝试了上述解决方案,但它不起作用,因为您无法添加的onTapGesture回报ViewText

Text("Some text ").foregroundColor(Color(UIColor.systemGray)) +
Text("clickable subtext")
   .foregroundColor(Color(UIColor.systemBlue))
   .onTapGesture {

   }
Run Code Online (Sandbox Code Playgroud)

我想在正文中有可点击的潜台词,这就是为什么使用HStack不起作用

Але*_*кий 19

iOS 15 及更高版本的更新: 有一个新的Markdown格式支持Text,例如:

Text("Some text [clickable subtext](some url) *italic ending* ")
Run Code Online (Sandbox Code Playgroud)

您可以使用时间码查看WWDC 会话以获取详细信息

iOS 13 和 14 的旧答案:

不幸的是,SwiftUI 中没有类似于 NSAttributedString 的东西。你只有几个选择。例如,在此答案中,您可以看到如何使用单击事件UIViewRepresentable创建老式学校。但现在唯一的 SwiftUI 方法是使用:UILabel HStack

Text("Some text [clickable subtext](some url) *italic ending* ")
Run Code Online (Sandbox Code Playgroud)

更新 使用UITextView和添加解决方案UIViewRepresentable。我结合了添加链接的所有内容,结果非常好,我认为:

struct TappablePieceOfText: View {
    
    var body: some View {
        
        HStack(spacing: 0) {
            Text("Go to ")
                .foregroundColor(.gray)

            Text("stack overflow")
                .foregroundColor(.blue)
                .underline()
                .onTapGesture {
                    let url = URL.init(string: "https://stackoverflow.com/")
                    guard let stackOverflowURL = url, UIApplication.shared.canOpenURL(stackOverflowURL) else { return }
                    UIApplication.shared.open(stackOverflowURL)
                }
            
            Text(" and enjoy")
                .foregroundColor(.gray)
        }
        
        
    }
}
Run Code Online (Sandbox Code Playgroud)

HStack和的结果TextHStack 和文本

UIViewRepresentable和的结果UITextView

在此处输入图片说明

更新2:这是一个NSMutableAttributedString小扩展:

extension NSMutableAttributedString {
    
    var range: NSRange {
        NSRange(location: 0, length: self.length)
    }
    
}
Run Code Online (Sandbox Code Playgroud)

  • 感谢您的回答,这个解决方案非常适合单行文本,但对于多行文本似乎没有 SwiftUI 的方法。 (2认同)
  • @АлександрГрабовский 嗨,扩展名是什么?没在答案中看到。谢谢! (2认同)

Ale*_*huk 8

从 iOS 15 开始,您可以将AttributedStringMarkdown与文本一起使用。

使用示例Markdown

Text("Plain text. [This is a tappable link](https://stackoverflow.com)")
Run Code Online (Sandbox Code Playgroud)

AttributedString让您可以更好地控制格式。例如,您可以更改链接颜色:

var string = AttributedString("Plain text. ")
        
var tappableText = AttributedString("I am tappable!")
tappableText.link = URL(string: "https://stackoverflow.com")
tappableText.foregroundColor = .green

string.append(tappableText)

Text(string)
Run Code Online (Sandbox Code Playgroud)

它看起来是这样的:

可点击的文本

旁注:如果您希望可点击文本具有与在浏览器中打开 URL 不同的行为,您可以为您的应用程序定义自定义 URL 方案。然后,您将能够使用onOpenURL(perform:)注册处理程序来处理链接上的点击事件,以便在视图收到视图所在场景或窗口的 url 时调用。


Zor*_*ayr 7

我没有耐心去制作UITextViewUIViewRepresentable工作,所以我让整个段落都可以点击,但仍然保持带下划线的 URL 外观/感觉。如果您尝试将服务条款 URL 链接添加到您的应用程序,则特别有用。

在此处输入图片说明

代码相当简单:

Button(action: {
    let tosURL = URL.init(string: "https://www.google.com")! // add your link here
    if UIApplication.shared.canOpenURL(tosURL) {
        UIApplication.shared.open(tosURL)
    }
}, label: {
    (Text("Store.ly helps you find storage units nearby. By continuing, you agree to our ")
        + Text("Terms of Service.")
            .underline()
        )
        .frame(maxWidth: .infinity, alignment: .leading)
        .font(Font.system(size: 14, weight: .medium))
        .foregroundColor(Color.black)
        .fixedSize(horizontal: false, vertical: true)
})
    .padding([.horizontal], 20)

Run Code Online (Sandbox Code Playgroud)

  • 感谢@Zorayr 的回答,这个解决方案也可能对某人有用。 (2认同)

小智 6

基于 Dhaval Bera 的代码,我放置了一些结构。

struct TextLabelWithHyperLink: UIViewRepresentable {
  
  @State var tintColor: UIColor
  
  @State var hyperLinkItems: Set<HyperLinkItem>
  
  private var _attributedString: NSMutableAttributedString
  
  private var openLink: (HyperLinkItem) -> Void
  
  init (
    tintColor: UIColor,
    string: String,
    attributes: [NSAttributedString.Key : Any],
    hyperLinkItems: Set<HyperLinkItem>,
    openLink: @escaping (HyperLinkItem) -> Void
  ) {
    self.tintColor = tintColor
    self.hyperLinkItems = hyperLinkItems
    self._attributedString = NSMutableAttributedString(
      string: string,
      attributes: attributes
    )
    self.openLink = openLink
  }
  
  
  func makeUIView(context: Context) -> UITextView {
    let textView = UITextView()
    textView.setContentCompressionResistancePriority(.defaultLow, for: .horizontal)
    textView.isEditable = false
    textView.isSelectable = true
    textView.tintColor = self.tintColor
    textView.delegate = context.coordinator
    textView.isScrollEnabled = false
    return textView
  }
  
  func updateUIView(_ uiView: UITextView, context: Context) {
   
    for item in hyperLinkItems {
      let subText = item.subText
      let link = item.subText.replacingOccurrences(of: " ", with: "_")
      
      _attributedString
        .addAttribute(
          .link,
          value: String(format: "https://%@", link),
          range: (_attributedString.string as NSString).range(of: subText)
        )
    }
    
    uiView.attributedText = _attributedString
  }
  
  func makeCoordinator() -> Coordinator {
    Coordinator(parent: self)
  }
  
  class Coordinator: NSObject, UITextViewDelegate {
    var parent : TextLabelWithHyperLink
    
    init( parent: TextLabelWithHyperLink ) {
      self.parent = parent
    }
    
    func textView(
      _ textView: UITextView,
      shouldInteractWith URL: URL,
      in characterRange: NSRange,
      interaction: UITextItemInteraction
    ) -> Bool {
      
      let strPlain = URL.absoluteString
        .replacingOccurrences(of: "https://", with: "")
        .replacingOccurrences(of: "_", with: " ")
      
      if let ret = parent.hyperLinkItems.first(where: { $0.subText == strPlain }) {
        parent.openLink(ret)
      }
      
      return false
    }
  }
}

struct HyperLinkItem: Hashable {
    
  let subText : String
  let attributes : [NSAttributedString.Key : Any]?
  
  init (
    subText: String,
    attributes: [NSAttributedString.Key : Any]? = nil
  ) {
    self.subText = subText
    self.attributes = attributes
  }
  
  func hash(into hasher: inout Hasher) {
    hasher.combine(subText)
  }
    
  static func == (lhs: HyperLinkItem, rhs: HyperLinkItem) -> Bool {
    lhs.hashValue == rhs.hashValue
  }
}
Run Code Online (Sandbox Code Playgroud)

用法:


TextLabelWithHyperLink(
  tintColor: .green,
  string: "Please contact us by filling contact form. We will contact with you shortly.  Your request will be processed in accordance with the Terms of Use and Privacy Policy.",
  attributes: [:],
  hyperLinkItems: [
    .init(subText: "processed"),
    .init(subText: "Terms of Use"),
  ],
  openLink: {
  (tappedItem) in
    print("Tapped link: \(tappedItem.subText)")
  }
)
Run Code Online (Sandbox Code Playgroud)