35 votes

Sous-texte SwiftUI à effleurer

Existe-t-il un moyen dans SwiftUI d'ouvrir le navigateur lorsque l'on tape sur une partie du texte.

J'ai essayé la solution ci-dessus mais elle ne fonctionne pas car onTapGesture retours View que vous ne pouvez pas ajouter à Text

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

   }

Je souhaite que le texte principal comporte un sous-texte accessible, c'est pourquoi l'utilisation de HStack ne fonctionnera pas

48voto

Malheureusement, il n'y a rien qui ressemble à NSAttributedString dans SwiftUI. Vous n'avez donc que peu d'options. Dans cette réponse vous pouvez voir comment utiliser UIViewRepresentable pour la création d'un UILabel avec événement de clic par exemple. Mais maintenant la seule façon SwiftUI est d'utiliser HStack :

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)
        }

    }
}

MISE À JOUR Solution ajoutée avec UITextView y UIViewRepresentable . J'ai combiné tous les liens ajoutés et le résultat est assez bon, je pense :

import SwiftUI
import UIKit

struct TappablePieceOfText: View {

    var body: some View {
        TextLabelWithHyperlink()
            .frame(width: 300, height: 110)
    }

}

struct TextLabelWithHyperlink: UIViewRepresentable {

    func makeUIView(context: Context) -> UITextView {

        let standartTextAttributes: [NSAttributedString.Key : Any] = [
            NSAttributedString.Key.font: UIFont.systemFont(ofSize: 20),
            NSAttributedString.Key.foregroundColor: UIColor.gray
        ]

        let attributedText = NSMutableAttributedString(string: "You can go to ")
        attributedText.addAttributes(standartTextAttributes, range: attributedText.range) // check extention

        let hyperlinkTextAttributes: [NSAttributedString.Key : Any] = [
            NSAttributedString.Key.font: UIFont.systemFont(ofSize: 20),
            NSAttributedString.Key.foregroundColor: UIColor.blue,
            NSAttributedString.Key.underlineStyle: NSUnderlineStyle.single.rawValue,
            NSAttributedString.Key.link: "https://stackoverflow.com"
        ]

        let textWithHyperlink = NSMutableAttributedString(string: "stack overflow site")
        textWithHyperlink.addAttributes(hyperlinkTextAttributes, range: textWithHyperlink.range)
        attributedText.append(textWithHyperlink)

        let endOfAttrString = NSMutableAttributedString(string: " end enjoy it using old-school UITextView and UIViewRepresentable")
        endOfAttrString.addAttributes(standartTextAttributes, range: endOfAttrString.range)
        attributedText.append(endOfAttrString)

        let textView = UITextView()
        textView.attributedText = attributedText

        textView.isEditable = false
        textView.textAlignment = .center
        textView.isSelectable = true

        return textView
    }

    func updateUIView(_ uiView: UITextView, context: Context) {}

}

résultat de HStack y Text : HStack and Text

résultat de UIViewRepresentable y UITextView :

enter image description here

MISE À JOUR 2 : voici un NSMutableAttributedString petite extension :

extension NSMutableAttributedString {

    var range: NSRange {
        NSRange(location: 0, length: self.length)
    }

}

13voto

Zorayr Points 2637

Je n'ai pas eu la patience de faire les UITextView y UIViewRepresentable J'ai donc préféré rendre l'ensemble du paragraphe tactile, tout en conservant l'aspect de l'URL soulignée. Particulièrement utile si vous essayez d'ajouter un lien URL vers les conditions d'utilisation à votre application.

enter image description here

Le code est assez simple :

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)

6voto

seulbeom kim Points 11

En me basant sur le code de Dhaval Bera, j'ai mis en place quelques structures.

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
  }
}

Utilisation :

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)")
  }
)

1voto

Dhaval Bera Points 26

Chaine de caractères apposable en utilisant UITextView

struct TextLabelWithHyperlink: UIViewRepresentable {

@State var tintColor: UIColor = UIColor.black
@State var arrTapableString: [String] = []

var configuration = { (view: UITextView) in }
var openlink = {(strtext: String) in}

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) {
    configuration(uiView)
    let stringarr  = NSMutableAttributedString(attributedString: uiView.attributedText)
    for strlink in arrTapableString{
        let link = strlink.replacingOccurrences(of: " ", with: "_")
        stringarr.addAttribute(.link, value: String(format: "https://%@", link), range: (stringarr.string as NSString).range(of: strlink))
    }
    uiView.attributedText = stringarr
}

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 (self.parent.arrTapableString.contains(strPlain)) {
            self.parent.openlink(strPlain)
        }
        return false
    }

}}

Mise en œuvre dans le cadre de swiftui

TextLabelWithHyperlink(arrTapableString: ["Terms of Use", "Privacy Policy"]) { (textView) in
                            let 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."

                            let attrib = NSMutableAttributedString(string: string, attributes: [.font: UIFont(name: Poodlife_Font.oxygen_regular, size: 14)!,.foregroundColor:  UIColor.black])

                            attrib.addAttributes([.font: UIFont(name: Font.oxygen_bold, size: 14)!,
                                                  .foregroundColor:  UIColor.black], range: (string as NSString).range(of: "Terms of Use"))

                            attrib.addAttributes([.font: UIFont(name: Font.oxygen_bold, size: 14)!,
                                                  .foregroundColor:  UIColor.black,
                                                  .link: "Privacy_Policy"], range: (string as NSString).range(of: "Privacy Policy"))

                            textView.attributedText = attrib
                        } openlink: { (tappedString) in
                            print("Tapped link:\(tappedString)")
                        }

0voto

J'ai utilisé la réponse @, mais j'ai également dû faire quelques configurations pour que cela fonctionne pour moi. J'ai 2 liens dans mon champ de texte, les deux ont une couleur personnalisée et dirigent l'utilisateur vers des pages différentes. Je ne voulais pas non plus que le scroll soit activé, mais si je le désactivais, la hauteur ne serait pas ajustée et le texte s'étirerait jusqu'à l'extérieur de la vue. J'ai essayé BEAUCOUP de choses différentes et j'ai trouvé, pour le moment, une solution qui fonctionne pour moi, alors j'ai pensé que je pourrais la partager ici.

This is what I was looking for

Encore une fois, grâce à la réponse @, j'ai réussi à le faire. Les seules modifications que j'ai dû apporter sont les suivantes :

  1. j'ai défini les attributs des liens relatifs à la couleur du texte dans une autre var et j'ai défini la propriété "linkTextAttributes" de l'UITextView dans cette var, afin de changer la couleur du texte, tandis que j'ai utilisé la police et la destination du lien comme suggéré dans sa réponse. La couleur du texte n'a pas changé si j'ai attribué les attributs de couleur au lien lui-même.

    let linkAttributes : [NSAttributedString.Key : Any] = [ NSAttributedString.Key.foregroundColor : UIColor(named : "my_custom_green") ? ? UIColor.blue ] textView.linkTextAttributes = linkAttributes

  2. Je ne voulais pas que l'UITextView défile et le seul moyen que j'ai trouvé pour conserver la hauteur de plusieurs lignes et ne pas défiler (la valeur false de isScrollEnabled n'a pas fonctionné pour moi) était de fixer scrollRangeToVisible à la dernière chaîne de caractères que j'avais.

    textView.scrollRangeToVisible(ppWithHyperlink.range)

Je ne sais pas si c'est la meilleure alternative, mais c'est ce que j'ai trouvé... j'espère qu'à l'avenir il y aura une meilleure façon de faire cela dans swiftUI !!!

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X