创建可点击的“链接” 在 UILabel 的 NSAttributedString 中?
许多应用程序都有文本,并且在该文本中是圆角矩形中的 Web 超链接。 当我单击它们时,UIWebView
打开。 让我困惑的是,它们经常有自定义链接,例如,如果单词以 # 开头,它也是可单击的,并且应用程序通过打开另一个视图来响应。 我怎样才能做到这一点? 是否可以使用 UILabel
或者我需要 UITextView
或其他东西?
Many applications have text and in this text are web hyperlinks in rounded rect. When I click them UIWebView
opens. What puzzles me is that they often have custom links, for example if words starts with # it is also clickable and the application responds by opening another view. How can I do that? Is it possible with UILabel
or do I need UITextView
or something else?
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(30)
标签 #Swift2.0
我从 - 优秀 - @NAlexN 的答案中获得灵感,我决定自己编写一个 UILabel 的包装器。
我也尝试了 TTTAttributedLabel 但我无法使其工作。
希望您能欣赏这段代码,欢迎任何建议!
TAGS #Swift2.0
I take inspiration on - excellent - @NAlexN's answer and I decide to write by myself a wrapper of UILabel.
I also tried TTTAttributedLabel but I can't make it works.
Hope you can appreciate this code, any suggestions are welcome!
一般来说,如果我们希望 UILabel 显示的文本中有一个可点击的链接,我们需要解决两个独立的任务:
第一个很简单。 从 iOS 6 开始 UILabel 支持显示属性字符串。 您需要做的就是创建并配置 NSMutableAttributedString 的实例:
就是这样! 上面的代码使 UILabel 显示 带有 链接 的字符串
现在我们应该检测此链接上的触摸。 这个想法是捕获 UILabel 内的所有点击,并确定点击的位置是否距离链接足够近。 为了捕捉触摸,我们可以向标签添加点击手势识别器。 确保为标签启用 userInteraction,默认情况下它是关闭的:
现在是最复杂的事情:查明点击是否位于显示链接的位置而不是标签的任何其他部分。 如果我们有单行 UILabel,则可以通过对显示链接的区域边界进行硬编码来相对容易地解决此任务,但让我们更优雅地解决此问题,对于一般情况 - 多行 UILabel,无需对链接布局有初步了解。
其中一种方法是使用 iOS 7 中引入的 Text Kit API 的功能:将
创建和配置的 NSLayoutManager、NSTextContainer 和 NSTextStorage 实例保存在类(很可能是 UIViewController 的后代)的属性中 - 我们将在其他方法中需要它们。
现在,每次标签更改其框架时,都会更新 textContainer 的大小:
最后,检测点击是否正好在链接上:
In general, if we want to have a clickable link in text displayed by UILabel, we would need to resolve two independent tasks:
The first one is easy. Starting from iOS 6 UILabel supports display of attributed strings. All you need to do is to create and configure an instance of NSMutableAttributedString:
That's it! The code above makes UILabel to display String with a link
Now we should detect touches on this link. The idea is to catch all taps within UILabel and figure out whether the location of the tap was close enough to the link. To catch touches we can add tap gesture recognizer to the label. Make sure to enable userInteraction for the label, it's turned off by default:
Now the most sophisticated stuff: finding out whether the tap was on where the link is displayed and not on any other portion of the label. If we had single-lined UILabel, this task could be solved relatively easy by hardcoding the area bounds where the link is displayed, but let's solve this problem more elegantly and for general case - multiline UILabel without preliminary knowledge about the link layout.
One of the approaches is to use capabilities of Text Kit API introduced in iOS 7:
Save created and configured instances of NSLayoutManager, NSTextContainer and NSTextStorage in properties in your class (most likely UIViewController's descendant) - we'll need them in other methods.
Now, each time the label changes its frame, update textContainer's size:
And finally, detect whether the tap was exactly on the link:
我正在扩展 @NAlexN 原始详细解决方案,使用 @zekel
UITapGestureRecognizer
的优秀扩展,并在 Swift 中提供。扩展 UITapGestureRecognizer
使用
设置
UIGestureRecognizer
以将操作发送到tapLabel:
,并且您可以检测myLabel
中是否正在点击目标范围。重要提示:
UILabel
换行模式必须设置为按字/字符换行。 不知何故,仅当换行模式为其他情况时,NSTextContainer
才会假定文本为单行。I am extending @NAlexN original detailed solution, with @zekel excellent extension of
UITapGestureRecognizer
, and providing in Swift.Extending UITapGestureRecognizer
Usage
Setup
UIGestureRecognizer
to send actions totapLabel:
, and you can detect if the target ranges is being tapped on inmyLabel
.IMPORTANT: The
UILabel
line break mode must be set to wrap by word/char. Somehow,NSTextContainer
will assume that the text is single line only if the line break mode is otherwise.老问题,但如果任何人都可以使用
UITextView
而不是UILabel
,那么这很容易。 标准 URL、电话号码等将被自动检测(并且可点击)。但是,如果您需要自定义检测,也就是说,如果您希望能够在用户单击特定单词后调用任何自定义方法,则需要将
NSAttributedStrings
与NSLinkAttributeName< /code> 属性将指向自定义 URL 方案(而不是默认的 http url 方案)。 Ray Wenderlich 已在此处介绍
引用上述链接中的代码:
要检测这些链接点击,请执行以下操作:
PS:确保您的
UITextView
是可选择
。Old question but if anyone can use a
UITextView
instead of aUILabel
, then it is easy. Standard URLs, phone numbers etc will be automatically detected (and be clickable).However, if you need custom detection, that is, if you want to be able to call any custom method after a user clicks on a particular word, you need to use
NSAttributedStrings
with anNSLinkAttributeName
attribute that will point to a custom URL scheme(as opposed to having the http url scheme by default). Ray Wenderlich has it covered hereQuoting the code from the aforementioned link:
To detect those link clicks, implement this:
PS: Make sure your
UITextView
isselectable
.(我的答案建立在@NAlexN的优秀答案的基础上。我不会在这里重复他对每个步骤的详细解释。)
我发现最方便和直接的方法是将可点击的 UILabel 文本作为类别添加到 UITapGestureRecognizer。(您不必使用 UITextView 的数据检测器,正如一些答案所建议的那样) .)
将以下方法添加到您的 UITapGestureRecognizer 类别中:
示例代码
手势回调
(My answer builds on @NAlexN's excellent answer. I won't duplicate his detailed explanation of each step here.)
I found it most convenient and straightforward to add support for tap-able UILabel text as a category to UITapGestureRecognizer. (You don't have to use UITextView's data detectors, as some answers suggest.)
Add the following method to your UITapGestureRecognizer category:
Example Code
Gesture Callback
如果您没有为其设置任何图像,则 UIButtonTypeCustom 是一个可单击的标签。
The UIButtonTypeCustom is a clickable label if you don't set any images for it.
将 @samwize 的扩展翻译为 Swift 4:
设置识别器(给文本和内容着色后):
...然后是手势识别器:
Translating @samwize's Extension to Swift 4:
To set up the recognizer (once you colored the text and stuff):
...then the gesture recognizer:
UITextView
支持 OS3.0 中的数据检测器,而UILabel
不支持。如果您在
UITextView
上启用数据检测器,并且您的文本包含 URL、电话号码等,它们将显示为链接。UITextView
supports data-detectors in OS3.0, whereasUILabel
doesn't.If you enable the data-detectors on the
UITextView
and your text contains URLs, phone numbers, etc. they will appear as links.最简单可靠的方法是按照 Kedar Paranjape 的建议使用 UITextView。 基于Karl Nosworthy的回答我终于想出了一个简单的UITextView子类:
用法非常简单:
注意isScrollEnabled 默认情况下为 false,因为在大多数情况下我们需要具有自己大小且不滚动的小型标签式视图。 如果您想要可滚动的文本视图,只需将其设置为 true 即可。
另请注意,与 UILabel 不同,UITextView 有默认的文本填充。 要删除它并使布局与 UILabel 中的布局相同,只需添加:
实现
onLinkTap
闭包不是必需的,没有它,URL 将由 UIApplication 自动打开。可以通过设置 UITextView 的
linkTextAttributes
属性来自定义链接外观。由于文本选择在大多数情况下是不可取的,但它无法关闭,因此在委托方法中被忽略(感谢 Carson Vo)
Most simple and reliable approach is to use UITextView as Kedar Paranjape recommended. Based on answer of Karl Nosworthy I finally came up with a simple UITextView subclass:
Usage is very simple:
Note that
isScrollEnabled
is false by default, as in most cases we need small label-like view with own size and without scrolling. Just set it to true if you want a scrollable text view.Also note that the UITextView unlike UILabel has default text padding. To remove it and make layout same as in UILabel just add:
Implementing
onLinkTap
closure is not necessary, without it URLs are automatically open by UIApplication.Links appearance can be customized by setting
linkTextAttributes
property of the UITextView.As Text selection is undesirable in most cases, but it can't be turned off it is dismissed in delegate method (Thanks to Carson Vo)
有些答案并没有像预期的那样对我有用。 这是 Swift 解决方案,还支持
textAlignment
和多行。 不需要子类化,只需这个UITapGestureRecognizer
扩展:用法:
Some answers didn't work for me as expected. This is Swift solution that supports also
textAlignment
and multiline. No subclassing needed, just thisUITapGestureRecognizer
extension:Usage:
这是 NAlexN 答案的快速版本。
然后,您可以在
viewDidLoad
方法中创建该类的实例,如下所示:最好有一个自定义属性,以便在角色被点击。 现在,它是 NSLinkAttributeName,但可以是任何内容,您可以使用该值执行除打开 url 之外的其他操作,您可以执行任何自定义操作。
Here is a swift version of NAlexN's answer.
You can then create an instance of that class inside your
viewDidLoad
method like this:It's better to have a custom attribute to use when a character is tapped. Now, it's the
NSLinkAttributeName
, but could be anything and you can use that value to do other things other than opening a url, you can do any custom action.我创建了名为 ResponsiveLabel 的 UILabel 子类,它基于 iOS 7 中引入的 textkit API。它使用相同的方法由 NAlexN 建议。 它提供了指定在文本中搜索的模式的灵活性。 人们可以指定要应用于这些图案的样式以及点击这些图案时要执行的操作。
如果你想让一个字符串可点击,你可以这样做。 此代码将属性应用于字符串“text”的每次出现。
I created UILabel subclass named ResponsiveLabel which is based on textkit API introduced in iOS 7. It uses the same approach suggested by NAlexN. It provides flexibility to specify a pattern to search in the text. One can specify styles to be applied to those patterns as well as action to be performed on tapping the patterns.
If you want to make a string clickable, you can do this way. This code applies attributes to each occurrence of the string "text".
以下是超链接 UILabel 的示例代码:
资料来源:http://sickprogrammersarea.blogspot.in/2014/ 03/adding-links-to-uilabel.html
Here is example code to hyperlink UILabel:
Source:http://sickprogrammersarea.blogspot.in/2014/03/adding-links-to-uilabel.html
在 Swift 3 中工作,将整个代码粘贴到此处
Worked in Swift 3, pasting the entire code here
正如我在这篇文章中提到的,
这是我专门为 UILabel FRHyperLabel 中的链接创建的轻量级库。
要实现这样的效果:
Lorem ipsum dolor sat amet, consectetur adipiscing elit。 Pellentesque 是 平淡无奇< /a> eros,坐上 amet 车辆 justo。 Nam at urna neque。 Maecenas ac sem eu sem porta dictum nec veltellus。
使用代码:
As I mentioned in this post,
here is a light-weighted library I created specially for links in UILabel FRHyperLabel.
To achieve an effect like this:
Lorem ipsum dolor sit amet, consectetur adipiscing elit. Pellentesque quis blandit eros, sit amet vehicula justo. Nam at urna neque. Maecenas ac sem eu sem porta dictum nec vel tellus.
use code:
这是一个尽可能简单的 Swift 实现,还包括触摸反馈。 注意事项:
"\u{a0}"
)添加属性来创建链接。链接
键。
Here’s a Swift implementation that is about as minimal as possible that also includes touch feedback. Caveats:
"\u{a0}"
).link
keys.
这是一个嵌入式 Objective-C 类别,它利用现有的
NSLinkAttributeName
属性,在现有UILabel.attributedText
字符串中启用可点击链接。通过 UILabel 子类(即没有 objc_getAssociatedObject 混乱)来完成这会更干净一些,但如果您像我一样,您更愿意避免创建不必要的(第 3 方)子类,只是为了向现有 UIKit 类添加一些额外的功能。 此外,它还有一个优点,即它向任何现有的 UILabel 添加可点击链接,例如现有的
UITableViewCells
!我尝试通过使用 NSAttributedString 中已有的现有
NSLinkAttributeName
属性内容,使其尽可能具有最小的侵入性。 所以它很简单:基本上,它通过向 UILabel 添加 UIGestureRecognizer 来工作。 艰苦的工作是在
gestureRecognizerShouldBegin:
中完成的,它重新布局 attributeText 字符串以找出点击了哪个字符。 如果此字符是 NSLinkAttributeName 的一部分,则gestureRecognizer 将随后触发,检索相应的 URL(从 NSLinkAttributeName 值),并按照通常的[UIApplication.sharedApplication openURL:url]
流程打开链接。注意 - 通过在
gestureRecognizerShouldBegin:
中执行所有这些操作,如果您没有碰巧点击标签中的链接,则事件将被传递。 因此,例如,您的 UITableViewCell 将捕获链接上的点击,但其他行为正常(选择单元格、取消选择、滚动...)。我已将其放入 此处 的 GitHub 存储库中。
改编自 Kai Burghardt 在此处发布的 SO。
Here's a drop-in Objective-C category that enables clickable links in existing
UILabel.attributedText
strings, exploiting the existingNSLinkAttributeName
attribute.This would be a bit cleaner done via a UILabel subclass (ie none of the objc_getAssociatedObject mess), but if you are like me you prefer to avoid having to make unnecessary (3rd party) subclasses just to add some extra function to existing UIKit classes. Also, this has the beauty that it adds clickable-links to any existing UILabel, eg existing
UITableViewCells
!I've tried to make it as minimally invasive as possible by using the existing
NSLinkAttributeName
attribute stuff already available in NSAttributedString. So its a simple as:Basically, it works by adding a
UIGestureRecognizer
to your UILabel. The hard work is done ingestureRecognizerShouldBegin:
, which re-layouts the attributedText string to find out which character was tapped on. If this character was part of a NSLinkAttributeName then the gestureRecognizer will subsequently fire, retrieve the corresponding URL (from the NSLinkAttributeName value), and open the link per the usual[UIApplication.sharedApplication openURL:url]
process.Note - by doing all this in
gestureRecognizerShouldBegin:
, if you dont happen to tap on a link in the label, the event is passed along. So, for example, your UITableViewCell will capture taps on links, but otherwise behave normally (select cell, unselect, scroll, ...).I've put this in a GitHub repository here.
Adapted from Kai Burghardt's SO posting here.
是的,这是可能的,尽管一开始很难弄清楚。 我将更进一步向您展示如何单击文本中的任何区域。
使用此方法,您可以拥有 UI 标签,即:
第 1 步:
使 UILabel 具有换行属性 '截断尾部'并设置最小字体比例。
如果您不熟悉字体比例,请记住以下规则:
minimumFontSize/defaultFontSize = fontscale
在我的例子中,我希望
7.2
为最小字体大小我的起始字体大小是36
。 因此,7.2 / 36 = 0.2
第 2 步:
如果您不关心标签是否可点击,而只是想要一个有效的多行标签就完成了!
但是,如果您希望标签可点击,请继续阅读...
添加我创建的以下扩展
它的使用方式如下(只需替换
< Label>
与您的实际标签名称):需要此扩展,因为自动收缩在自动收缩后不会更改标签的“字体”属性,因此您必须通过以下方式推断它计算它的方式与使用 .size(withAttributes) 函数相同,该函数模拟该特定字体的大小。
这是必要的,因为检测标签点击位置的解决方案需要知道确切的字体大小。
第 3 步:
添加以下扩展:
您需要针对特定的多线情况修改此扩展。 就我而言,您会注意到我使用了段落样式。
请务必在扩展程序中将其更改为您实际使用的行间距,以便一切都能正确计算。
步骤4:
将gestureRecognizer添加到
viewDidLoad
中的标签或您认为合适的位置(只需再次将替换为您的标签名称:
这是一个简化的我的 tapLabel 函数的示例(只需将
替换为您的 UILabel 名称):
在我的示例中请注意,我的字符串是
BED = N * d * [ RBE + ( d / (α/β) ) ]
,因此在本例中我只是获取了α/β
的范围,您可以在字符串中添加“\n”以添加换行符,然后。无论您想要什么文本,并对其进行测试以在下一行中查找字符串,它仍然会找到它并正确检测点击,就这样,您完成了多行! /em> 标签。
Yes this is possible albeit very confusing to figure out at first. I will go a step further and show you how you can even click on any area in the text as well.
With this method you can have UI Label tha is:
Step 1:
Make the UILabel have the properties for Line Break of 'Truncate Tail' and set a minimum font scale.
If you are unfamiliar with font scale just remember this rule:
minimumFontSize/defaultFontSize = fontscale
In my case I wanted
7.2
to be the minimum font size and my starting font size was36
. Therefore,7.2 / 36 = 0.2
Step 2:
If you do not care about the labels being clickable and just wanted a working multiline label you are done!
HOWEVER, if you want the labels to be clickable read on...
Add this following extension I created
It's used like this (just replace
<Label>
with your actual label name):This extension is needed because auto shrink does NOT change the 'font' property of the label after it auto-shrinks so you have to deduce it by calculating it the same way it does by using .size(withAttributes) function which simulates what it's size would be with that particular font.
This is necessary because the solution for detecting where to click on the label requires the exact font size to be known.
Step 3:
Add the following extension:
You will need to modify this extension for your particular multiline situation. In my case you will notice that I use a paragraph style.
Make sure to change this in the extension to what you are actually using for your line spacing so that everything calculates correctly.
Step 4:
Add the gestureRecognizer to the label in
viewDidLoad
or where you think is appropriate like so (just replace<Label>
with your label name again:Here is a simplified example of my tapLabel function (just replace
<Label>
with your UILabel name):Just a note in my example, my string is
BED = N * d * [ RBE + ( d / (α/β) ) ]
, so I was just getting the range of theα/β
in this case. You could add "\n" to the string to add a newline and whatever text you wanted after and test this to find a string on the next line and it will still find it and detect the click correctly!That's it! You are done. Enjoy a multiline clickable label.
我们将 zekel 中的便捷解决方案与
UITapGestureRecognizer
类别结合使用。这使用
NSTextContainer
,就像这个问题的许多答案一样。但是,这返回了错误的字符索引。 显然是因为
NSTextContainer
缺少有关字体样式的信息,如其他帖子所示:之后更改:
至:
结果明显更好,点击区域现在正确地从目标字符串的第一个字符开始。 但最后一个字符仍然返回
NO
。 我们预计这与我们的目标字符串具有将字体粗细设置为 UIFontWeightSemibold 的属性有关。 虽然上述代码改进将label.font
应用于 整个 字符串,但该字符串具有常规权重。为了解决这个问题,我们通过迭代所有属性范围进一步改进了上面的代码片段,以支持文本中的多种字体样式:
现在该方法返回
YES
半粗体字符串范围内的每个字符,这是预期的结果。We use the convenient solution from zekel with the
UITapGestureRecognizer
category.This uses
NSTextContainer
, like many answers to this question do.However, this returned wrong character indexes. Apparently because
NSTextContainer
was missing information about the font style, as indicated by these other posts:After changing:
To:
The result was significantly better, the tap area was now correctly starting at the first character of our target string. But the last character returned still
NO
. We expected that this was related to the fact that our target string has attributes to set the font weight toUIFontWeightSemibold
. While the above code improvement applieslabel.font
on the entire string, which has a regular weight.To solve this we improved the above code snippet even further, by iterating over all attributed ranges, in order to support multiple font styles in the text:
Now the method returns
YES
for every character in the semi-bold string range, which is the expected result.使用以下 .h 和 .m 文件创建该类。 在 .m 文件中,有以下函数。
在该函数内,我们将检查需要为其提供操作的子字符串的范围。 使用你自己的逻辑来设置你的范围。
下面是子类的用法
下面是.h文件
下面是.m文件
Create the class with the following .h and .m files. In the .m file there is the following function
Inside this function we will check the ranges of substrings for which we need to give actions. Use your own logic to put your ranges.
And following is the usage of the subclass
following is the .h file
following is the .m file
我找到了另一个解决方案:
我找到了一种方法来检测从互联网上找到的html文本中的链接,将其转换为nsattributeString:
我的方法允许您检测超链接而无需指定它们。
首先创建 tapgesturerecognizer 的扩展:
}
然后在视图控制器中创建一个 url 和范围列表来存储属性文本包含的所有链接和范围:
查找 URL 和您可以使用的 URLRange :
然后您实现手柄 tap :
我们开始吧!
我希望这个解决方案可以帮助您,就像它对我一样。
I found a other solution:
I find a way to detect the link in a html text that you find from the internet you transform it into nsattributeString with :
My method allows you to detect the hyperlink without having to specify them.
first you create an extension of the tapgesturerecognizer :
}
then in you view controller you created a list of url and ranges to store all the links and the range that the attribute text contain:
to find the URL and the URLRange you can use :
then you implementing the handle tap :
and here we go!
I hope this solution help you like it help me.
就像之前的答案中报道的那样,UITextView 能够处理链接上的触摸。 通过将文本的其他部分用作链接,可以轻松扩展此功能。 AttributedTextView 库是 UITextView 的子类,可以非常轻松地处理这些内容。 有关更多信息,请参阅: https://github.com/evermeer/AttributedTextView
您可以将文本像这样交互(其中textView1是UITextView IBOutlet):
并且为了处理主题标签和提及,您可以使用如下代码:
Like there is reported in earlier answer the UITextView is able to handle touches on links. This can easily be extended by making other parts of the text work as links. The AttributedTextView library is a UITextView subclass that makes it very easy to handle these. For more info see: https://github.com/evermeer/AttributedTextView
You can make any part of the text interact like this (where textView1 is a UITextView IBOutlet):
And for handling hashtags and mentions you can use code like this:
我正在扩展 @samwize 的答案来处理多行 UILabel 并给出一个关于使用 UIButton 的示例
I'm extending @samwize's answer to handle multi-line UILabel and give an example on using for a UIButton
对于完全自定义的链接,您需要使用 UIWebView - 您可以拦截调用,以便在按下链接时可以转到应用程序的其他部分。
For fully custom links, you'll need to use a UIWebView - you can intercept the calls out, so that you can go to some other part of your app instead when a link is pressed.
根据 Charles Gamble 的回答,这是我使用的(我删除了一些让我困惑并给我错误索引的行):
based on Charles Gamble answer, this what I used (I removed some lines that confused me and gave me wrong indexed) :
我遵循这个版本,
Swift 4:
调用示例:
I follow this version,
Swift 4:
Call Example:
作为
UILabel
上的类别的嵌入式解决方案(假设您的UILabel
使用带有一些NSLinkAttributeName
属性的属性字符串):Drop-in solution as a category on
UILabel
(this assumes yourUILabel
uses an attributed string with someNSLinkAttributeName
attributes in it):这里是我基于@Luca Davanzo的answer,覆盖
touchesBegan
事件而不是点击手势:Here is my answer based on @Luca Davanzo's answer, override the
touchesBegan
event instead of a tap gesture:这个通用方法也有效!
你可以用以下方法调用该方法
This generic method works too !
And you can call the method with
修改了 @timbroder 代码以正确处理多行,以便 swift4.2
UILabel 代码
识别 Tap 的代码
Modified @timbroder code to handle multiple line correctly for swift4.2
UILabel Code
Code to recognise the Tap