许多应用程序都有文本,文本中是圆角矩形的web超链接,当我点击它们时,UIWebView就会打开。让我困惑的是,他们经常有自定义链接,例如,如果单词以#开头,它也是可点击的,应用程序通过打开另一个视图来响应。我该怎么做呢?是否可以用UILabel或者我需要UITextView或者其他什么?
当前回答
斯威夫特5.2
我在之前的回答中发现了多行文本标签的几个问题,所以我给出了我最终的工作解决方案。
它解决了多行和文本对齐的问题。
func didTapAttributedTextInLabel(label: UILabel, inRange targetRange: NSRange) -> Bool {
// Create instances of NSLayoutManager, NSTextContainer and NSTextStorage
let layoutManager = NSLayoutManager()
let textContainer = NSTextContainer(size: CGSize.zero)
let textStorage = NSTextStorage(attributedString: label.attributedText!)
// Configure layoutManager and textStorage
layoutManager.addTextContainer(textContainer)
textStorage.addLayoutManager(layoutManager)
// Configure textContainer
textContainer.lineFragmentPadding = 0.0
textContainer.lineBreakMode = label.lineBreakMode
textContainer.maximumNumberOfLines = label.numberOfLines
let labelSize = label.bounds.size
textContainer.size = labelSize
// Find the tapped character location and compare it to the specified range
let locationOfTouchInLabel = self.location(in: label)
let textBoundingBox = layoutManager.usedRect(for: textContainer)
let textAligmentOffset = aligmentOffset(for: label)
let textContainerOffset = CGPoint(x: (labelSize.width - textBoundingBox.size.width) * textAligmentOffset - textBoundingBox.origin.x,
y: (labelSize.height - textBoundingBox.size.height) * textAligmentOffset - textBoundingBox.origin.y)
let locationOfTouchInTextContainer = CGPoint(x: (locationOfTouchInLabel.x - textContainerOffset.x),
y: 0 )
// Adjust for multiple lines of text
let lineModifier = Int(floor(locationOfTouchInLabel.y / label.font.lineHeight)) - 1
let rightMostFirstLinePoint = CGPoint(x: labelSize.width,
y: 0)
let charsPerLine = layoutManager.characterIndex(for: rightMostFirstLinePoint, in: textContainer, fractionOfDistanceBetweenInsertionPoints: nil)
let indexOfCharacter = layoutManager.characterIndex(for: locationOfTouchInTextContainer, in: textContainer, fractionOfDistanceBetweenInsertionPoints: nil)
let adjustedRange = indexOfCharacter + (lineModifier * charsPerLine)
return NSLocationInRange(adjustedRange, targetRange)
}
private func aligmentOffset(for label: UILabel) -> CGFloat {
switch label.textAlignment {
case .left, .natural, .justified:
return 0.0
case .center:
return 0.5
case .right:
return 1.0
@unknown default:
return 0.0
}
}
其他回答
UIButtonTypeCustom是一个可点击的标签,如果你没有为它设置任何图像。
以下是基于@Luca Davanzo的回答,重写touchesBegan事件而不是轻触手势:
import UIKit
public protocol TapableLabelDelegate: NSObjectProtocol {
func tapableLabel(_ label: TapableLabel, didTapUrl url: String, atRange range: NSRange)
}
public class TapableLabel: UILabel {
private var links: [String: NSRange] = [:]
private(set) var layoutManager = NSLayoutManager()
private(set) var textContainer = NSTextContainer(size: CGSize.zero)
private(set) var textStorage = NSTextStorage() {
didSet {
textStorage.addLayoutManager(layoutManager)
}
}
public weak var delegate: TapableLabelDelegate?
public override var attributedText: NSAttributedString? {
didSet {
if let attributedText = attributedText {
textStorage = NSTextStorage(attributedString: attributedText)
} else {
textStorage = NSTextStorage()
links = [:]
}
}
}
public override var lineBreakMode: NSLineBreakMode {
didSet {
textContainer.lineBreakMode = lineBreakMode
}
}
public override var numberOfLines: Int {
didSet {
textContainer.maximumNumberOfLines = numberOfLines
}
}
public override init(frame: CGRect) {
super.init(frame: frame)
setup()
}
public required init?(coder aDecoder: NSCoder) {
super.init(coder: aDecoder)
setup()
}
public override func layoutSubviews() {
super.layoutSubviews()
textContainer.size = bounds.size
}
/// addLinks
///
/// - Parameters:
/// - text: text of link
/// - url: link url string
public func addLink(_ text: String, withURL url: String) {
guard let theText = attributedText?.string as? NSString else {
return
}
let range = theText.range(of: text)
guard range.location != NSNotFound else {
return
}
links[url] = range
}
private func setup() {
isUserInteractionEnabled = true
layoutManager.addTextContainer(textContainer)
textContainer.lineFragmentPadding = 0
textContainer.lineBreakMode = lineBreakMode
textContainer.maximumNumberOfLines = numberOfLines
}
public override func touchesBegan(_ touches: Set<UITouch>, with event: UIEvent?) {
guard let locationOfTouch = touches.first?.location(in: self) else {
return
}
textContainer.size = bounds.size
let indexOfCharacter = layoutManager.glyphIndex(for: locationOfTouch, in: textContainer)
for (urlString, range) in links {
if NSLocationInRange(indexOfCharacter, range), let url = URL(string: urlString) {
delegate?.tapableLabel(self, didTapUrl: urlString, atRange: range)
}
}
}}
我创建了一个名为ResponsiveLabel的UILabel子类,它是基于ios7中引入的textkit API。它使用了NAlexN建议的相同方法。它提供了在文本中指定搜索模式的灵活性。可以指定应用于这些模式的样式,以及在敲击模式时要执行的操作。
//Detects email in text
NSString *emailRegexString = @"[A-Z0-9._%+-]+@[A-Z0-9.-]+\\.[A-Z]{2,4}";
NSError *error;
NSRegularExpression *regex = [[NSRegularExpression alloc]initWithPattern:emailRegexString options:0 error:&error];
PatternDescriptor *descriptor = [[PatternDescriptor alloc]initWithRegex:regex withSearchType:PatternSearchTypeAll withPatternAttributes:@{NSForegroundColorAttributeName:[UIColor redColor]}];
[self.customLabel enablePatternDetection:descriptor];
如果你想让一个字符串可点击,你可以这样做。这段代码将属性应用到字符串“text”的每个出现处。
PatternTapResponder tapResponder = ^(NSString *string) {
NSLog(@"tapped = %@",string);
};
[self.customLabel enableStringDetection:@"text" withAttributes:@{NSForegroundColorAttributeName:[UIColor redColor],
RLTapResponderAttributeName: tapResponder}];
我强烈建议使用自动检测文本中的url并将其转换为链接的库。 试一试:
TTTAttributedLabel (pod) ZSWTappableLabel (under).
两者都得到了麻省理工学院的许可。
我扩展了@samwize的答案来处理多行UILabel,并给出了一个使用UIButton的例子
extension UITapGestureRecognizer {
func didTapAttributedTextInButton(button: UIButton, inRange targetRange: NSRange) -> Bool {
guard let label = button.titleLabel else { return false }
return didTapAttributedTextInLabel(label, inRange: targetRange)
}
func didTapAttributedTextInLabel(label: UILabel, inRange targetRange: NSRange) -> Bool {
// Create instances of NSLayoutManager, NSTextContainer and NSTextStorage
let layoutManager = NSLayoutManager()
let textContainer = NSTextContainer(size: CGSize.zero)
let textStorage = NSTextStorage(attributedString: label.attributedText!)
// Configure layoutManager and textStorage
layoutManager.addTextContainer(textContainer)
textStorage.addLayoutManager(layoutManager)
// Configure textContainer
textContainer.lineFragmentPadding = 0.0
textContainer.lineBreakMode = label.lineBreakMode
textContainer.maximumNumberOfLines = label.numberOfLines
let labelSize = label.bounds.size
textContainer.size = labelSize
// Find the tapped character location and compare it to the specified range
let locationOfTouchInLabel = self.locationInView(label)
let textBoundingBox = layoutManager.usedRectForTextContainer(textContainer)
let textContainerOffset = CGPointMake((labelSize.width - textBoundingBox.size.width) * 0.5 - textBoundingBox.origin.x,
(labelSize.height - textBoundingBox.size.height) * 0.5 - textBoundingBox.origin.y);
let locationOfTouchInTextContainer = CGPointMake((locationOfTouchInLabel.x - textContainerOffset.x),
0 );
// Adjust for multiple lines of text
let lineModifier = Int(ceil(locationOfTouchInLabel.y / label.font.lineHeight)) - 1
let rightMostFirstLinePoint = CGPointMake(labelSize.width, 0)
let charsPerLine = layoutManager.characterIndexForPoint(rightMostFirstLinePoint, inTextContainer: textContainer, fractionOfDistanceBetweenInsertionPoints: nil)
let indexOfCharacter = layoutManager.characterIndexForPoint(locationOfTouchInTextContainer, inTextContainer: textContainer, fractionOfDistanceBetweenInsertionPoints: nil)
let adjustedRange = indexOfCharacter + (lineModifier * charsPerLine)
return NSLocationInRange(adjustedRange, targetRange)
}
}
推荐文章
- 如何删除默认的导航栏空间在SwiftUI导航视图
- 如何在iOS中使用Swift编程segue
- Swift -整数转换为小时/分钟/秒
- Swift:声明一个空字典
- 我如何用javascript编程点击链接?
- 在成功提交我的应用程序后,“太多符号文件”
- 首先添加一个UIView,甚至是导航栏
- 我如何改变UIButton标题颜色?
- 在Swift中如何调用GCD主线程上的参数方法?
- NSLayoutConstraints是可动画的吗?
- iOS -构建失败,CocoaPods无法找到头文件
- CFNetwork SSLHandshake iOS 9失败
- 请求失败:不可接受的内容类型:文本/html使用AFNetworking 2.0
- 缺少推荐的图标文件-该包不包含iPhone / iPod Touch的应用程序图标,像素为“120x120”,png格式
- 以编程方式创建segue