Como criar um link clicável em um NSAttributedString?

200

É trivial tornar os hiperlinks clicáveis ​​em um arquivo UITextView. Você acabou de definir a caixa de seleção "detectar links" na exibição no IB, e ele detecta os links HTTP e os transforma em hiperlinks.

No entanto, isso ainda significa que o que o usuário vê é o link "bruto". Arquivos RTF e HTML permitem que você configure uma sequência legível pelo usuário com um link "por trás" dela.

É fácil instalar o texto atribuído em uma exibição de texto (ou um UILabelou UITextField, nesse caso.) No entanto, quando esse texto atribuído inclui um link, ele não pode ser clicado.

Existe uma maneira de tornar o texto legível pelo usuário clicável em um UITextView, UILabelou UITextField?

A marcação é diferente no SO, mas aqui está a ideia geral. O que eu quero é um texto como este:

Essa transformação foi gerada com o Face Dancer , Clique para visualizar na loja de aplicativos.

A única coisa que posso obter é esta:

Essa transformação foi gerada com o Face Dancer. Clique em http://example.com/facedancer para visualizar na loja de aplicativos.

Duncan C
fonte
Tente este exemplo .. IFTweetLabel Espero que ajude ..
Vidhyanand
Bom trabalho ultrapassando 100K em um piscar de olhos. Bem-vindo ao clube 100K. Bem merecido!
vacawama
@vacawama, espere, quando isso aconteceu? Eu estava em 98k na última vez que olhei! (I ouvir rumores de que você obter alguns ganhos SO como um membro do clube de 100k?)
Duncan C
Eles mudaram os votos positivos nas perguntas de +5 para +10; portanto, se você tivesse 800 votos positivos, obteria +4000 em um piscar de olhos. Ainda estou esperando 100 mil ganhos (cruzados em abril). Alguma coisa sobre a mudança de fornecedores de
muamba

Respostas:

156

Use NSMutableAttributedString .

NSMutableAttributedString * str = [[NSMutableAttributedString alloc] initWithString:@"Google"];
[str addAttribute: NSLinkAttributeName value: @"http://www.google.com" range: NSMakeRange(0, str.length)];
yourTextView.attributedText = str;

Editar :

Não se trata diretamente da questão, mas apenas para esclarecer UITextFielde UILabelnão suporta a abertura de URLs. Se você deseja usar UILabelcom links, pode verificar TTTAttributedLabel .

Além disso, você deve definir dataDetectorTypeso valor do seu UITextViewpara UIDataDetectorTypeLinkou UIDataDetectorTypeAllpara abrir URLs quando clicado. Ou você pode usar o método delegado, conforme sugerido nos comentários.

ujell
fonte
7
Sim, ele está trabalhando, basta colocá-lo dentro de um método UITextView e delegado override: - (BOOL) textView: (UITextView *) textView shouldInteractWithURL: (NSURL *) url inRange: (NSRange) characterRange
Yunus Nedim Mehel
Isso não funciona em um UILabel - nada acontece quando você toca no campo.
Jack BeNimble
7
@saboehnke, você quer dizer chamar um método quando o link foi clicado? se assim for implementar o método delegado, dar um manequim url como atributo e chamar seu método em- (BOOL)textView:(UITextView *)textView shouldInteractWithURL:(NSURL *)URL inRange:(NSRange)characterRange
ujell
2
Não sei como está funcionando. O valor do atributo deve ser tipo de NSURL. ----[str addAttribute: NSLinkAttributeName value: [NSURL URLWithString:@"http://www.google.com"] range: NSMakeRange(0, str.length)];
Nirav Dangi
1
@NiravDangi deNSAttributedString.h UIKIT_EXTERN NSString * const NSLinkAttributeName NS_AVAILABLE(10_0, 7_0); // NSURL (preferred) or NSString
Ahmed Nawar 17/11/2015
142

Achei isso realmente útil, mas eu precisava fazer isso em alguns lugares, então envolvi minha abordagem em uma extensão simples para NSMutableAttributedString:

Swift 3

extension NSMutableAttributedString {

    public func setAsLink(textToFind:String, linkURL:String) -> Bool {

        let foundRange = self.mutableString.range(of: textToFind)
        if foundRange.location != NSNotFound {
            self.addAttribute(.link, value: linkURL, range: foundRange)
            return true
        }
        return false
    }
}

Swift 2

import Foundation

extension NSMutableAttributedString {

   public func setAsLink(textToFind:String, linkURL:String) -> Bool {

       let foundRange = self.mutableString.rangeOfString(textToFind)
       if foundRange.location != NSNotFound {
           self.addAttribute(NSLinkAttributeName, value: linkURL, range: foundRange)
           return true
       }
       return false
   }
}

Exemplo de uso:

let attributedString = NSMutableAttributedString(string:"I love stackoverflow!")
let linkWasSet = attributedString.setAsLink("stackoverflow", linkURL: "http://stackoverflow.com")

if linkWasSet {
    // adjust more attributedString properties
}

Objetivo-C

Acabei de atingir um requisito para fazer o mesmo em um projeto Objective-C puro, então aqui está a categoria Objective-C.

@interface NSMutableAttributedString (SetAsLinkSupport)

- (BOOL)setAsLink:(NSString*)textToFind linkURL:(NSString*)linkURL;

@end


@implementation NSMutableAttributedString (SetAsLinkSupport)

- (BOOL)setAsLink:(NSString*)textToFind linkURL:(NSString*)linkURL {

     NSRange foundRange = [self.mutableString rangeOfString:textToFind];
     if (foundRange.location != NSNotFound) {
         [self addAttribute:NSLinkAttributeName value:linkURL range:foundRange];
         return YES;
     }
     return NO;
}

@end

Exemplo de uso:

NSMutableAttributedString *attributedString = [[NSMutableAttributedString alloc] initWithString:"I love stackoverflow!"];

BOOL linkWasSet = [attributedString setAsLink:@"stackoverflow" linkURL:@"http://stackoverflow.com"];

if (linkWasSet) {
    // adjust more attributedString properties
}

Verifique se o atributo de comportamento do NSTextField está definido como selecionável. Atributo de comportamento Xcode NSTextField

Karl Nosworthy
fonte
Um exemplo rápido de uso / implementação disso seria muito apreciado.
ioopl
3
@ioop. Adicionei um exemplo muito pequeno ao post original acima, espero que ajude.
31416 Karl Nosworthy
7
Isso funcionou corretamente. Só quero dizer que você precisa fazer o seu selecionável UITextView para permitir que o link seja clicável
lujop
1
@felecia genet, nas implementações Objective C e Swift, o método retorna um resultado booleano para indicar se ocorreu uma correspondência e um conjunto resultante. O erro que você está vendo é porque você não está capturando esse resultado - o que é bom. Você pode capturar esse resultado atribuindo-o a uma variável local ou ajustar o método para impedi-lo de retornar o valor booleano, se melhor atender às suas necessidades. Espero que ajude?
Karl Nosworthy
1
Sem problemas @feleciagenet, adicionei o armazenamento e a verificação do resultado do método nos exemplos Swift e ObjectiveC.
Karl Nosworthy
34

Acabei de criar uma subclasse de UILabel para abordar especialmente esses casos de uso. Você pode adicionar vários links facilmente e definir diferentes manipuladores para eles. Ele também suporta o destaque do link pressionado quando você toca no touchdown para obter feedback. Consulte https://github.com/null09264/FRHyperLabel .

No seu caso, o código pode ser assim:

FRHyperLabel *label = [FRHyperLabel new];

NSString *string = @"This morph was generated with Face Dancer, Click to view in the app store.";
NSDictionary *attributes = @{NSFontAttributeName: [UIFont preferredFontForTextStyle:UIFontTextStyleHeadline]};

label.attributedText = [[NSAttributedString alloc]initWithString:string attributes:attributes];

[label setLinkForSubstring:@"Face Dancer" withLinkHandler:^(FRHyperLabel *label, NSString *substring){
    [[UIApplication sharedApplication] openURL:aURL];
}];

Captura de tela de amostra (o manipulador está configurado para exibir um alerta em vez de abrir um URL neste caso)

confrontador

Jinghan Wang
fonte
se o meu texto for assim Este morph foi gerado com o Face Dancer, clique na visualização Click to Face Dancer na loja de aplicativos Face Dancer. aqui eu estou tendo 3 Dançarino Facial não estava funcionando para ele
Manchikanti KRISHNAKISHORE
1
Nesse caso, use a API - (void)setLinkForRange:(NSRange)range withLinkHandler:(void(^)(FRHyperLabel *label, NSRange selectedRange))handler; . Por favor, consulte o leia-me na página do github.
Jinghan Wang
1
FRHyperLabel parece não estar mais funcionando. Dentro de "characterIndexForPoint:", ele sempre retorna -1 (não encontrado).
John Pang
Não funciona para mim no rótulo de várias linhas. A detecção de caracteres está incorreta. 15-caracteres ligar string é clicável apenas em alguns primeiros caracteres, outros personagens não fazer nada
ACCID brilhante
27

Pequena melhoria na solução da ujell: se você usa NSURL em vez de um NSString, pode usar qualquer URL (por exemplo, URLs personalizados)

NSURL *URL = [NSURL URLWithString: @"whatsapp://app"];
NSMutableAttributedString * str = [[NSMutableAttributedString alloc] initWithString:@"start Whatsapp"];
[str addAttribute: NSLinkAttributeName value:URL range: NSMakeRange(0, str.length)];
yourTextField.attributedText = str;

Diverta-se!

Hans One
fonte
21

Swift 4:

var string = "Google"
var attributedString = NSMutableAttributedString(string: string, attributes:[NSAttributedStringKey.link: URL(string: "http://www.google.com")!])

yourTextView.attributedText = attributedString

Swift 3.1:

var string = "Google"
var attributedString = NSMutableAttributedString(string: string, attributes:[NSLinkAttributeName: URL(string: "http://www.google.com")!])

yourTextView.attributedText = attributedString
Bill Chan
fonte
Esta resposta funciona perfeitamente como está. Não parece precisar de nenhuma das subclasses coloridas ou personalizadas usadas por outras respostas.
zeroimpl
19

Eu também tinha um requisito semelhante, inicialmente usei o UILabel e depois percebi que o UITextView é melhor. Eu fiz o UITextView se comportar como o UILabel, desativando a interação e a rolagem, e criei um método de categoria para NSMutableAttributedStringdefinir o link para o texto, exatamente como o que Karl havia feito (+1 para isso).

-(void)setTextAsLink:(NSString*) textToFind withLinkURL:(NSString*) url
{
    NSRange range = [self.mutableString rangeOfString:textToFind options:NSCaseInsensitiveSearch];

    if (range.location != NSNotFound) {

        [self addAttribute:NSLinkAttributeName value:url range:range];
        [self addAttribute:NSForegroundColorAttributeName value:[UIColor URLColor] range:range];
    }
}

você pode usar o delegado abaixo para lidar com a ação

- (BOOL)textView:(UITextView *)textView shouldInteractWithURL:(NSURL *)url inRange:(NSRange)characterRange
{
    // do the task
    return YES;
}
anoop4real
fonte
1
Tanto quanto eu posso dizer configuração NSForegroundColorAttributeNameem um intervalo onde NSLinkAttributeNameé aplicado não funciona. Não importa o quê, os linkTextAttributesde UITextViewsão aplicados. Funciona NSForegroundColorAttributeNamepara você?
Dima
Tem certeza de que também não está definindo linkTextAttributesa mesma coisa? ou talvez tintColor? Você consegue fazer 2 links aparecerem em cores diferentes na mesma visualização de texto?
Dima
1
Aqui está um código de trabalho NSRange range = [self.text rangeOfString: textToFind options: NSCaseInsensitiveSearch]; if (range.location! = NSNotFound) {NSMutableAttributedString * string = [[alocação do NSMutableAttributedString] initWithString: self.text]; [string addAttribute: NSLinkAttributeName valor: url range: range]; [string addAttribute: NSForegroundColorAttributeName valor: [UIColor blueColor] range: range]; self.text = @ ""; self.attributedText = string; }
Nosov Pavel 10/07/2015
16

Use o UITextView, que suporta links clicáveis. Crie uma sequência atribuída usando o seguinte código

NSMutableAttributedString *attributedString = [[NSMutableAttributedString alloc] initWithString:strSomeTextWithLinks];

Em seguida, defina o texto UITextView da seguinte maneira

NSDictionary *linkAttributes = @{NSForegroundColorAttributeName: [UIColor redColor],

                                 NSUnderlineColorAttributeName: [UIColor blueColor],

                                 NSUnderlineStyleAttributeName: @(NSUnderlinePatternSolid)};

customTextView.linkTextAttributes = linkAttributes; // customizes the appearance of links
textView.attributedText = attributedString;

Certifique-se de ativar o comportamento "Selecionável" do UITextView no XIB.

Nitheesh George
fonte
15
Eu acho que essa é a melhor solução! A nota sobre a ativação Selectableé importante!
precisa saber é o seguinte
Isso não sublinhou o link para mim (iOS 7, 8). Eu precisava usar NSUnderlineStyleAttributeName: [NSNumber numberWithInt: NSUnderlineStyleSingle]
prewett
1
torná-lo selecionável é a informação mais importante e não intuitiva!
Nicolas Massart 28/09
13

O cerne da minha pergunta era que eu queria criar links clicáveis ​​em exibições / campos / rótulos de texto sem precisar escrever um código personalizado para manipular o texto e adicionar os links. Eu queria que fosse orientado a dados.

Finalmente descobri como fazê-lo. A questão é que o IB não respeita os links incorporados.

Além disso, a versão iOS do NSAttributedStringnão permite inicializar uma sequência atribuída a partir de um arquivo RTF. A versão OS X do NSAttributedString faz ter um inicializador que leva um arquivo RTF como entrada.

NSAttributedString está em conformidade com o protocolo NSCoding, para que você possa convertê-lo para / de NSData

Criei uma ferramenta de linha de comando do OS X que usa um arquivo RTF como entrada e gera um arquivo com a extensão .data que contém o NSData do NSCoding. Em seguida, coloco o arquivo .data no meu projeto e adiciono algumas linhas de código que carregam o texto na exibição. O código fica assim (este projeto estava no Swift):

/*
If we can load a file called "Dates.data" from the bundle and convert it to an attributed string,
install it in the dates field. The contents contain clickable links with custom URLS to select
each date.
*/
if
  let datesPath = NSBundle.mainBundle().pathForResource("Dates", ofType: "data"),
  let datesString = NSKeyedUnarchiver.unarchiveObjectWithFile(datesPath) as? NSAttributedString
{
  datesField.attributedText = datesString
}

Para aplicativos que usam muito texto formatado, crio uma regra de construção que informa ao Xcode que todos os arquivos .rtf em uma determinada pasta são de origem e os arquivos de dados são a saída. Depois de fazer isso, basta adicionar arquivos .rtf ao diretório designado (ou editar arquivos existentes) e o processo de compilação descobre que eles são novos / atualizados, executa a ferramenta de linha de comando e copia os arquivos no pacote de aplicativos. Funciona lindamente.

Eu escrevi uma postagem no blog com links para um projeto de amostra (Swift) demonstrando a técnica. Você pode vê-lo aqui:

Criando URLs clicáveis ​​em um UITextField que é aberto no seu aplicativo

Duncan C
fonte
11

Exemplo Swift 3 para detectar ações em toques de texto atribuídos

https://stackoverflow.com/a/44226491/5516830

let termsAndConditionsURL = TERMS_CONDITIONS_URL;
let privacyURL            = PRIVACY_URL;

override func viewDidLoad() {
    super.viewDidLoad()

    self.txtView.delegate = self
    let str = "By continuing, you accept the Terms of use and Privacy policy"
    let attributedString = NSMutableAttributedString(string: str)
    var foundRange = attributedString.mutableString.range(of: "Terms of use") //mention the parts of the attributed text you want to tap and get an custom action
    attributedString.addAttribute(NSLinkAttributeName, value: termsAndConditionsURL, range: foundRange)
    foundRange = attributedString.mutableString.range(of: "Privacy policy")
    attributedString.addAttribute(NSLinkAttributeName, value: privacyURL, range: foundRange)
    txtView.attributedText = attributedString
}

func textView(_ textView: UITextView, shouldInteractWith URL: URL, in characterRange: NSRange) -> Bool {
    let storyboard = UIStoryboard(name: "Main", bundle: nil)
    let vc = storyboard.instantiateViewController(withIdentifier: "WebView") as! SKWebViewController

    if (URL.absoluteString == termsAndConditionsURL) {
        vc.strWebURL = TERMS_CONDITIONS_URL
        self.navigationController?.pushViewController(vc, animated: true)
    } else if (URL.absoluteString == privacyURL) {
        vc.strWebURL = PRIVACY_URL
        self.navigationController?.pushViewController(vc, animated: true)
    }
    return false
}

Da mesma forma, você pode adicionar qualquer ação que desejar com o shouldInteractWith URLmétodo UITextFieldDelegate.

Felicidades!!

Akila Wasala
fonte
7

A resposta rápida é usar UITextView em vez de UILabel. Você precisa ativar Selectablee desativar Editable.

Desative os indicadores de rolagem e salte.

Captura de tela

Captura de tela

Minha solução usando NSMutableAttributedStringfrom html stringNSHTMLTextDocumentType

NSString *s = @"<p><a href='https://itunes.apple.com/us/app/xxxx/xxxx?mt=8'>https://itunes.apple.com/us/app/xxxx/xxxx?mt=8</a></p>";

NSMutableAttributedString *text = [[NSMutableAttributedString alloc]
                                           initWithData: [s dataUsingEncoding:NSUnicodeStringEncoding]
                                           options: @{ NSDocumentTypeDocumentAttribute: NSHTMLTextDocumentType }
                                           documentAttributes: nil
                                           error: nil
                                           ];

cell.content.attributedText = text;
Dody Rachmat Wicaksono
fonte
Este. Consegui ler um arquivo RTF do meu pacote de recursos, convertê-lo em NSAttributedString, defini-lo como o attributedTextmeu UITextViewe os hiperlinks funcionam! Daria muito trabalho encontrar o intervalo de cada hiperlink e configurá-lo usando atributos.
Nicolas Miari
6

Eu escrevi um método, que adiciona um link (linkString) a uma string (fullString) com um determinado URL (urlString):

- (NSAttributedString *)linkedStringFromFullString:(NSString *)fullString withLinkString:(NSString *)linkString andUrlString:(NSString *)urlString
{
    NSRange range = [fullString rangeOfString:linkString options:NSLiteralSearch];
    NSMutableAttributedString *str = [[NSMutableAttributedString alloc] initWithString:fullString];

    NSMutableParagraphStyle *paragraphStyle = NSMutableParagraphStyle.new;
    paragraphStyle.alignment = NSTextAlignmentCenter;
    NSDictionary *attributes = @{NSForegroundColorAttributeName:RGB(0x999999),
                                 NSFontAttributeName:[UIFont fontWithName:@"HelveticaNeue-Light" size:10],
                                 NSParagraphStyleAttributeName:paragraphStyle};
    [str addAttributes:attributes range:NSMakeRange(0, [str length])];
    [str addAttribute: NSLinkAttributeName value:urlString range:range];

    return str;
}

Você deve chamar assim:

NSString *fullString = @"A man who bought the Google.com domain name for $12 and owned it for about a minute has been rewarded by Google for uncovering the flaw.";
NSString *linkString = @"Google.com";
NSString *urlString = @"http://www.google.com";

_youTextView.attributedText = [self linkedStringFromFullString:fullString withLinkString:linkString andUrlString:urlString];
Denis Kutlubaev
fonte
É clicável, mas não abre o link nem nada. apenas clica como um botão que não faz nada.
Reza.Ab
5

Eu precisava continuar usando um UILabel puro, assim chamado pelo meu reconhecedor de toque (isso é baseado na resposta de malex aqui: Índice de caracteres no ponto de contato do UILabel )

UILabel* label = (UILabel*)gesture.view;
CGPoint tapLocation = [gesture locationInView:label];

// create attributed string with paragraph style from label

NSMutableAttributedString* attr = [label.attributedText mutableCopy];
NSMutableParagraphStyle* paragraphStyle = [NSMutableParagraphStyle new];
paragraphStyle.alignment = label.textAlignment;

[attr addAttribute:NSParagraphStyleAttributeName value:paragraphStyle range:NSMakeRange(0, label.attributedText.length)];

// init text storage

NSTextStorage *textStorage = [[NSTextStorage alloc] initWithAttributedString:attr];
NSLayoutManager *layoutManager = [[NSLayoutManager alloc] init];
[textStorage addLayoutManager:layoutManager];

// init text container

NSTextContainer *textContainer = [[NSTextContainer alloc] initWithSize:CGSizeMake(label.frame.size.width, label.frame.size.height+100) ];
textContainer.lineFragmentPadding  = 0;
textContainer.maximumNumberOfLines = label.numberOfLines;
textContainer.lineBreakMode        = label.lineBreakMode;

[layoutManager addTextContainer:textContainer];

// find tapped character

NSUInteger characterIndex = [layoutManager characterIndexForPoint:tapLocation
                                                  inTextContainer:textContainer
                         fractionOfDistanceBetweenInsertionPoints:NULL];

// process link at tapped character

[attr enumerateAttributesInRange:NSMakeRange(characterIndex, 1)
                                         options:0
                                      usingBlock:^(NSDictionary<NSString *,id> * _Nonnull attrs, NSRange range, BOOL * _Nonnull stop) {
                                          if (attrs[NSLinkAttributeName]) {
                                              NSString* urlString = attrs[NSLinkAttributeName];
                                              NSURL* url = [NSURL URLWithString:urlString];
                                              [[UIApplication sharedApplication] openURL:url];
                                          }
                                      }];
masty
fonte
Isso foi bastante útil, não consegui obter índices de caracteres na última linha. Seu código possui os +100 no textContainer ao iniciar o CGSize, o que não faz muito sentido para mim, mas funcionou.
blueether
4

Atualizar:

Havia duas partes principais da minha pergunta:

  1. Como criar um link em que o texto mostrado para o link clicável seja diferente do link real que é chamado:
  2. Como configurar os links sem precisar usar código personalizado para definir os atributos no texto.

Acontece que o iOS 7 adicionou a capacidade de carregar o texto atribuído NSData.

Criei uma subclasse personalizada UITextViewque aproveita o @IBInspectableatributo e permite carregar o conteúdo de um arquivo RTF diretamente no IB. Você simplesmente digita o nome do arquivo no IB e a classe personalizada faz o resto.

Aqui estão os detalhes:

No iOS 7, NSAttributedStringganhou o métodoinitWithData:options:documentAttributes:error: . Esse método permite carregar um NSAttributedString a partir de um objeto NSData. Você pode primeiro carregar um arquivo RTF no NSData e depois usá-lo initWithData:options:documentAttributes:error:para carregá-lo na exibição de texto. (Observe que também há um método initWithFileURL:options:documentAttributes:error:que carrega uma sequência atribuída diretamente de um arquivo, mas esse método foi preterido no iOS 9. É mais seguro usar o método initWithData:options:documentAttributes:error:, que não foi preterido.

Eu queria um método que me permitisse instalar links clicáveis ​​nos meus modos de exibição de texto sem precisar criar nenhum código específico para os links que eu estava usando.

A solução que encontrei foi criar uma subclasse personalizada do UITextView que eu chamo RTF_UITextViewe fornecer uma@IBInspectable propriedade chamada RTF_Filename. A adição do @IBInspectableatributo a uma propriedade faz com que o Interface Builder exponha essa propriedade no "Attributes Inspector". Em seguida, você pode definir esse valor no IB sem código personalizado.

Também adicionei um @IBDesignableatributo à minha classe personalizada. o@IBDesignable atributo diz ao Xcode que ele deve instalar uma cópia em execução da sua classe de visualização customizada no construtor Interface, para que você possa vê-la na exibição gráfica da sua hierarquia de visualizações. () Infelizmente, para esta classe, a @IBDesignablepropriedade parece ser esquisita. Funcionou quando o adicionei pela primeira vez, mas excluí o conteúdo de texto sem formatação da minha exibição de texto e os links clicáveis ​​desapareceram e não consegui recuperá-los.)

O código para o meu RTF_UITextViewé muito simples. Além de adicionar o @IBDesignableatributo e uma RTF_Filenamepropriedade ao @IBInspectableatributo, adicionei um didSet()método à RTF_Filenamepropriedade O didSet()método é chamado sempre que o valor da RTF_Filenamepropriedade é alterado. O código para o didSet()método é bastante simples:

@IBDesignable
class RTF_UITextView: UITextView
{
  @IBInspectable
  var RTF_Filename: String?
    {
    didSet(newValue)
    {
      //If the RTF_Filename is nil or the empty string, don't do anything
      if ((RTF_Filename ?? "").isEmpty)
      {
        return
      }
      //Use optional binding to try to get an URL to the
      //specified filename in the app bundle. If that succeeds, try to load
      //NSData from the file.
      if let fileURL = NSBundle.mainBundle().URLForResource(RTF_Filename, withExtension: "rtf"),
        
        //If the fileURL loads, also try to load NSData from the URL.
        let theData = NSData(contentsOfURL: fileURL)
      {
        var aString:NSAttributedString
        do
        {
          //Try to load an NSAttributedString from the data
          try
            aString = NSAttributedString(data: theData,
              options: [:],
              documentAttributes:  nil
          )
          //If it succeeds, install the attributed string into the field.
          self.attributedText = aString;
        }
        catch
        {
          print("Nerp.");
        }
      }
      
    }
  }
}

Observe que, se a propriedade @IBDesignable não permitir a visualização confiável do texto com estilo no construtor de interface, será melhor configurar o código acima como uma extensão do UITextView em vez de uma subclasse personalizada. Dessa forma, você pode usá-lo em qualquer visualização de texto sem precisar alterar a visualização de texto para a classe personalizada.

Veja minha outra resposta se precisar oferecer suporte a versões do iOS anteriores ao iOS 7.

Você pode baixar um projeto de amostra que inclua essa nova classe no gitHub:

Projeto de demonstração DatesInSwift no Github

Duncan C
fonte
3

Basta encontrar uma solução sem código para o UITextView: insira a descrição da imagem aqui

Ative a detecção-> opções de links, o URL e também o email serão detectados e clicáveis!

Bill Chan
fonte
3
Isso torna os links clicáveis. Eu quero ter um texto legível pelo usuário que tenha um link por trás dele. Veja o exemplo na minha pergunta original.
Duncan C
Sim, minha resposta se aplica apenas ao caso em que o link é igual ao texto. Se o link for outra coisa, eu seguiria a resposta de @ ujell.
Bill Chan
3
Minha pergunta foi muito específica sobre o texto clicável que exibe algo diferente do URL. Você não fez mais do que olhar para a pergunta, fez?
Duncan C
1
não serviu a outros propósitos, mas com certeza é isso que vim para empilhar procurando ... uma maneira de tornar clicáveis ​​os links no meu aplicativo de bate-papo. Bingo Encontrei este artigo ... obrigado! Gostaria que o xcode permitisse a ativação do twitter e da hash tag.
MizAkita
Isso funciona mesmo com o texto personalizado instalado no link não processado. Lembre-se de selecionar Comportamento -> Selecionável e Detecção -> Links.
Krlbsk # 23/18
3

Versão rápida:

    // Attributed String for Label
    let plainText = "Apkia"
    let styledText = NSMutableAttributedString(string: plainText)
    // Set Attribuets for Color, HyperLink and Font Size
    let attributes = [NSFontAttributeName: UIFont.systemFontOfSize(14.0), NSLinkAttributeName:NSURL(string: "http://apkia.com/")!, NSForegroundColorAttributeName: UIColor.blueColor()]
    styledText.setAttributes(attributes, range: NSMakeRange(0, plainText.characters.count))
    registerLabel.attributedText = styledText
ioopl
fonte
3

Use UITextView e defina dataDetectorTypes para Link.

como isso:

testTextView.editable = false 
testTextView.dataDetectorTypes = .link

Se você deseja detectar um link, número de telefone, endereço etc.

testTextView.dataDetectorTypes = .all
Adarsh ​​GJ
fonte
3
Não. Isso permite que você torne os links clicáveis. Minha pergunta é específica para tornar um texto arbitrário como "clique aqui" clicável, não um URL como #http://somedomain/someurl?param=value
Duncan C
2

Uma rápida adição à descrição original de Duncan C sobre o comportamento de IB. Ele escreve: "É trivial tornar os hiperlinks clicáveis ​​em um UITextView. Você acabou de definir a caixa de seleção" detectar links "na exibição no IB, e ele detecta os links http e os transforma em hiperlinks".

Minha experiência (pelo menos no xcode 7) é que você também precisa desmarcar o comportamento "Editável" para que os URLs sejam detectados e clicáveis.

sakumatto
fonte
2

Caso você esteja tendo problemas com o que @Karl Nosworthy e @esilver haviam fornecido acima, atualizei a extensão NSMutableAttributedString para sua versão Swift 4.

extension NSMutableAttributedString {

public func setAsLink(textToFind:String, linkURL:String) -> Bool {

    let foundRange = self.mutableString.range(of: textToFind)
    if foundRange.location != NSNotFound {
         _ = NSMutableAttributedString(string: textToFind)
        // Set Attribuets for Color, HyperLink and Font Size
        let attributes = [NSFontAttributeName: UIFont.bodyFont(.regular, shouldResize: true), NSLinkAttributeName:NSURL(string: linkURL)!, NSForegroundColorAttributeName: UIColor.blue]

        self.setAttributes(attributes, range: foundRange)
        return true
    }
    return false
  }
}
Vick Swift
fonte
0

Se você deseja usar o NSLinkAttributeName em um UITextView, considere usar a biblioteca AttributedTextView. É uma subclasse UITextView que facilita muito o manuseio deles. Para mais informações, consulte: https://github.com/evermeer/AttributedTextView

Você pode fazer com que qualquer parte do texto interaja assim (onde textView1 é um UITextView IBoutlet):

textView1.attributer =
    "1. ".red
    .append("This is the first test. ").green
    .append("Click on ").black
    .append("evict.nl").makeInteract { _ in
        UIApplication.shared.open(URL(string: "http://evict.nl")!, options: [:], completionHandler: { completed in })
    }.underline
    .append(" for testing links. ").black
    .append("Next test").underline.makeInteract { _ in
        print("NEXT")
    }
    .all.font(UIFont(name: "SourceSansPro-Regular", size: 16))
    .setLinkColor(UIColor.purple) 

E para lidar com hashtags e menções, você pode usar código como este:

textView1.attributer = "@test: What #hashtags do we have in @evermeer #AtributedTextView library"
    .matchHashtags.underline
    .matchMentions
    .makeInteract { link in
        UIApplication.shared.open(URL(string: "https://twitter.com\(link.replacingOccurrences(of: "@", with: ""))")!, options: [:], completionHandler: { completed in })
    }
Edwin Vermeer
fonte
0
NSMutableAttributedString *attributedString = [[NSMutableAttributedString alloc] initWithString:strSomeTextWithLinks];

NSDictionary *linkAttributes = @{NSForegroundColorAttributeName: [UIColor redColor],   
                                 NSUnderlineColorAttributeName: [UIColor blueColor],
                                 NSUnderlineStyleAttributeName: @(NSUnderlinePatternSolid)};

customTextView.linkTextAttributes = linkAttributes; // customizes the appearance of links
textView.attributedText = attributedString;

PONTOS CHAVE:

  • Certifique-se de ativar o comportamento "Selecionável" do UITextView no XIB.
  • Certifique-se de desabilitar o comportamento "Editável" do UITextView no XIB.
Shashank Sharma
fonte