몇 시간 동안 이것을 검색했지만 실패했습니다. 아마 내가 무엇을 찾아야할지 모르겠다.
많은 응용 프로그램에는 텍스트가 있으며이 텍스트에는 둥근 사각형의 웹 하이퍼 링크가 있습니다. 클릭하면 UIWebView
열립니다. 예를 들어 단어가 #으로 시작하는 경우 # 클릭 할 수 있고 응용 프로그램이 다른보기를 열어 응답하는 경우가 종종 있습니다. 어떻게해야합니까? 가능 UILabel
하거나 필요 UITextView
하거나 다른 것입니까?
답변
일반적으로 UILabel에 의해 표시되는 텍스트로 클릭 가능한 링크를 가지려면 두 가지 독립적 인 작업을 해결해야합니다.
- 링크처럼 보이도록 텍스트 일부의 모양 변경
- 링크 터치 감지 및 처리 (URL 열기는 특별한 경우)
첫 번째는 쉽습니다. iOS 6부터 UILabel은 속성 문자열 표시를 지원합니다. NSMutableAttributedString 인스턴스를 생성하고 구성하기 만하면됩니다.
NSMutableAttributedString *attributedString = [[NSMutableAttributedString alloc] initWithString:@"String with a link" attributes:nil];
NSRange linkRange = NSMakeRange(14, 4); // for the word "link" in the string above
NSDictionary *linkAttributes = @{ NSForegroundColorAttributeName : [UIColor colorWithRed:0.05 green:0.4 blue:0.65 alpha:1.0],
NSUnderlineStyleAttributeName : @(NSUnderlineStyleSingle) };
[attributedString setAttributes:linkAttributes range:linkRange];
// Assign attributedText to UILabel
label.attributedText = attributedString;
그게 다야! 위의 코드는 UILabel 이 링크 가있는 문자열 을 표시 하도록 합니다.
이제이 링크의 터치를 감지해야합니다. 아이디어는 UILabel 내의 모든 탭을 잡고 탭의 위치가 링크에 충분히 가까운 지 알아내는 것입니다. 터치를 잡기 위해 탭 제스처 인식기를 라벨에 추가 할 수 있습니다. 레이블에 대해 userInteraction을 활성화하십시오. 기본적으로 꺼져 있습니다.
label.userInteractionEnabled = YES;
[label addGestureRecognizer:[[UITapGestureRecognizer alloc] initWithTarget:self action:@selector(handleTapOnLabel:)]];
가장 정교한 것 : 탭의 다른 부분이 아닌 링크가 표시된 위치에 탭이 있는지 확인하십시오. 우리가 한 줄짜리 UILabel을 가지고 있다면,이 작업은 링크가 표시되는 영역 경계를 하드 코딩하여 비교적 쉽게 해결할 수 있지만,이 문제를보다 우아하고 일반적인 경우-링크 레이아웃에 대한 사전 지식이없는 여러 줄 UILabel을 해결해 보겠습니다.
접근 방식 중 하나는 iOS 7에 도입 된 Text Kit API의 기능을 사용하는 것입니다.
// Create instances of NSLayoutManager, NSTextContainer and NSTextStorage
NSLayoutManager *layoutManager = [[NSLayoutManager alloc] init];
NSTextContainer *textContainer = [[NSTextContainer alloc] initWithSize:CGSizeZero];
NSTextStorage *textStorage = [[NSTextStorage alloc] initWithAttributedString:attributedString];
// Configure layoutManager and textStorage
[layoutManager addTextContainer:textContainer];
[textStorage addLayoutManager:layoutManager];
// Configure textContainer
textContainer.lineFragmentPadding = 0.0;
textContainer.lineBreakMode = label.lineBreakMode;
textContainer.maximumNumberOfLines = label.numberOfLines;
NSLayoutManager, NSTextContainer 및 NSTextStorage의 생성 및 구성된 인스턴스를 클래스의 속성 (대부분 UIViewController의 하위 항목)에 저장합니다. 다른 방법에서도 필요합니다.
이제 레이블이 프레임을 변경할 때마다 textContainer의 크기를 업데이트하십시오.
- (void)viewDidLayoutSubviews
{
[super viewDidLayoutSubviews];
self.textContainer.size = self.label.bounds.size;
}
마지막으로 탭이 정확히 링크에 있는지 여부를 감지하십시오.
- (void)handleTapOnLabel:(UITapGestureRecognizer *)tapGesture
{
CGPoint locationOfTouchInLabel = [tapGesture locationInView:tapGesture.view];
CGSize labelSize = tapGesture.view.bounds.size;
CGRect textBoundingBox = [self.layoutManager usedRectForTextContainer:self.textContainer];
CGPoint textContainerOffset = CGPointMake((labelSize.width - textBoundingBox.size.width) * 0.5 - textBoundingBox.origin.x,
(labelSize.height - textBoundingBox.size.height) * 0.5 - textBoundingBox.origin.y);
CGPoint locationOfTouchInTextContainer = CGPointMake(locationOfTouchInLabel.x - textContainerOffset.x,
locationOfTouchInLabel.y - textContainerOffset.y);
NSInteger indexOfCharacter = [self.layoutManager characterIndexForPoint:locationOfTouchInTextContainer
inTextContainer:self.textContainer
fractionOfDistanceBetweenInsertionPoints:nil];
NSRange linkRange = NSMakeRange(14, 4); // it's better to save the range somewhere when it was originally used for marking link in attributed string
if (NSLocationInRange(indexOfCharacter, linkRange)) {
// Open an URL, or handle the tap on the link in any other way
[[UIApplication sharedApplication] openURL:[NSURL URLWithString:@"https://stackoverflow.com/"]];
}
}
답변
@zekel의 뛰어난 확장 기능 으로 @NAlexN 독창적 인 세부 솔루션을 확장 하고 Swift 에서 제공하고 있습니다.UITapGestureRecognizer
.
UITapGestureRecognizer 확장
extension UITapGestureRecognizer {
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 textContainerOffset = CGPoint(
x: (labelSize.width - textBoundingBox.size.width) * 0.5 - textBoundingBox.origin.x,
y: (labelSize.height - textBoundingBox.size.height) * 0.5 - textBoundingBox.origin.y
)
let locationOfTouchInTextContainer = CGPoint(
x: locationOfTouchInLabel.x - textContainerOffset.x,
y: locationOfTouchInLabel.y - textContainerOffset.y
)
let indexOfCharacter = layoutManager.characterIndex(for: locationOfTouchInTextContainer, in: textContainer, fractionOfDistanceBetweenInsertionPoints: nil)
return NSLocationInRange(indexOfCharacter, targetRange)
}
}
용법
에 UIGestureRecognizer
작업을 보내도록 설정 하고 tapLabel:
에서 대상 범위를 탭하고 있는지 감지 할 수 있습니다 myLabel
.
@IBAction func tapLabel(gesture: UITapGestureRecognizer) {
if gesture.didTapAttributedTextInLabel(myLabel, inRange: targetRange1) {
print("Tapped targetRange1")
} else if gesture.didTapAttributedTextInLabel(myLabel, inRange: targetRange2) {
print("Tapped targetRange2")
} else {
print("Tapped none")
}
}
중요 : UILabel
줄 바꿈 모드는 단어 / 문자로 줄 바꿈되도록 설정해야합니다. 어떻게 든 NSTextContainer
줄 바꿈 모드가 아닌 경우에만 텍스트가 한 줄이라고 가정합니다.
답변
오래된 질문이지만 누군가가 UITextView
대신 사용할 수 있다면UILabel
쉽습니다. 표준 URL, 전화 번호 등이 자동으로 감지되고 클릭 가능합니다.
그러나 사용자 정의 감지가 필요한 경우, 즉 사용자가 특정 단어를 클릭 한 후 사용자 정의 메소드를 호출하려면 사용자 정의 URL 스킴을 가리키는 속성 NSAttributedStrings
과 함께 사용해야 NSLinkAttributeName
합니다 ( http URL 체계를 기본적으로 사용). 레이 웬더 리치가 여기에서 다뤘습니다
위에서 언급 한 링크에서 코드 인용 :
NSMutableAttributedString *attributedString = [[NSMutableAttributedString alloc] initWithString:@"This is an example by @marcelofabri_"];
[attributedString addAttribute:NSLinkAttributeName
value:@"username://marcelofabri_"
range:[[attributedString string] rangeOfString:@"@marcelofabri_"]];
NSDictionary *linkAttributes = @{NSForegroundColorAttributeName: [UIColor greenColor],
NSUnderlineColorAttributeName: [UIColor lightGrayColor],
NSUnderlineStyleAttributeName: @(NSUnderlinePatternSolid)};
// assume that textView is a UITextView previously created (either by code or Interface Builder)
textView.linkTextAttributes = linkAttributes; // customizes the appearance of links
textView.attributedText = attributedString;
textView.delegate = self;
이러한 링크 클릭을 감지하려면 다음을 구현하십시오.
- (BOOL)textView:(UITextView *)textView shouldInteractWithURL:(NSURL *)URL inRange:(NSRange)characterRange {
if ([[URL scheme] isEqualToString:@"username"]) {
NSString *username = [URL host];
// do something with this username
// ...
return NO;
}
return YES; // let the system open this URL
}
추신 : 물론 당신의 확인 UITextView
IS를 selectable
.
답변
UIButtonTypeCustom은 이미지를 설정하지 않은 경우 클릭 가능한 레이블입니다.
답변
(내 답변은 @NAlexN의 탁월한 답변을 기반으로 합니다. 각 단계에 대한 자세한 설명은 여기에 중복되지 않습니다.)
탭 가능한 UILabel 텍스트에 대한 지원을 UITapGestureRecognizer의 범주로 추가하는 것이 가장 편리하고 간단하다는 것을 알았습니다. (당신은 가지고 있지 않습니다 일부 답변 제안으로, UITextView의 데이터 감지기를 사용합니다.)
UITapGestureRecognizer 범주에 다음 방법을 추가하십시오.
/**
Returns YES if the tap gesture was within the specified range of the attributed text of the label.
*/
- (BOOL)didTapAttributedTextInLabel:(UILabel *)label inRange:(NSRange)targetRange {
NSParameterAssert(label != nil);
CGSize labelSize = label.bounds.size;
// create instances of NSLayoutManager, NSTextContainer and NSTextStorage
NSLayoutManager *layoutManager = [[NSLayoutManager alloc] init];
NSTextContainer *textContainer = [[NSTextContainer alloc] initWithSize:CGSizeZero];
NSTextStorage *textStorage = [[NSTextStorage alloc] initWithAttributedString:label.attributedText];
// configure layoutManager and textStorage
[layoutManager addTextContainer:textContainer];
[textStorage addLayoutManager:layoutManager];
// configure textContainer for the label
textContainer.lineFragmentPadding = 0.0;
textContainer.lineBreakMode = label.lineBreakMode;
textContainer.maximumNumberOfLines = label.numberOfLines;
textContainer.size = labelSize;
// find the tapped character location and compare it to the specified range
CGPoint locationOfTouchInLabel = [self locationInView:label];
CGRect textBoundingBox = [layoutManager usedRectForTextContainer:textContainer];
CGPoint textContainerOffset = CGPointMake((labelSize.width - textBoundingBox.size.width) * 0.5 - textBoundingBox.origin.x,
(labelSize.height - textBoundingBox.size.height) * 0.5 - textBoundingBox.origin.y);
CGPoint locationOfTouchInTextContainer = CGPointMake(locationOfTouchInLabel.x - textContainerOffset.x,
locationOfTouchInLabel.y - textContainerOffset.y);
NSInteger indexOfCharacter = [layoutManager characterIndexForPoint:locationOfTouchInTextContainer
inTextContainer:textContainer
fractionOfDistanceBetweenInsertionPoints:nil];
if (NSLocationInRange(indexOfCharacter, targetRange)) {
return YES;
} else {
return NO;
}
}
예제 코드
// (in your view controller)
// create your label, gesture recognizer, attributed text, and get the range of the "link" in your label
myLabel.userInteractionEnabled = YES;
[myLabel addGestureRecognizer:
[[UITapGestureRecognizer alloc] initWithTarget:self
action:@selector(handleTapOnLabel:)]];
// create your attributed text and keep an ivar of your "link" text range
NSAttributedString *plainText;
NSAttributedString *linkText;
plainText = [[NSMutableAttributedString alloc] initWithString:@"Add label links with UITapGestureRecognizer"
attributes:nil];
linkText = [[NSMutableAttributedString alloc] initWithString:@" Learn more..."
attributes:@{
NSForegroundColorAttributeName:[UIColor blueColor]
}];
NSMutableAttributedString *attrText = [[NSMutableAttributedString alloc] init];
[attrText appendAttributedString:plainText];
[attrText appendAttributedString:linkText];
// ivar -- keep track of the target range so you can compare in the callback
targetRange = NSMakeRange(plainText.length, linkText.length);
제스처 콜백
// handle the gesture recognizer callback and call the category method
- (void)handleTapOnLabel:(UITapGestureRecognizer *)tapGesture {
BOOL didTapLink = [tapGesture didTapAttributedTextInLabel:myLabel
inRange:targetRange];
NSLog(@"didTapLink: %d", didTapLink);
}
답변
UITextView
OS3.0에서 데이터 탐지기를 지원하지만 UILabel
그렇지 않습니다.
에서 데이터 탐지기를 사용하도록 설정하고 UITextView
텍스트에 URL, 전화 번호 등이 포함되어 있으면 링크로 나타납니다.
답변
@samwize의 확장 기능을 Swift 4로 변환 :
extension UITapGestureRecognizer {
func didTapAttributedTextInLabel(label: UILabel, inRange targetRange: NSRange) -> Bool {
guard let attrString = label.attributedText else {
return false
}
let layoutManager = NSLayoutManager()
let textContainer = NSTextContainer(size: .zero)
let textStorage = NSTextStorage(attributedString: attrString)
layoutManager.addTextContainer(textContainer)
textStorage.addLayoutManager(layoutManager)
textContainer.lineFragmentPadding = 0
textContainer.lineBreakMode = label.lineBreakMode
textContainer.maximumNumberOfLines = label.numberOfLines
let labelSize = label.bounds.size
textContainer.size = labelSize
let locationOfTouchInLabel = self.location(in: label)
let textBoundingBox = layoutManager.usedRect(for: textContainer)
let textContainerOffset = CGPoint(x: (labelSize.width - textBoundingBox.size.width) * 0.5 - textBoundingBox.origin.x, y: (labelSize.height - textBoundingBox.size.height) * 0.5 - textBoundingBox.origin.y)
let locationOfTouchInTextContainer = CGPoint(x: locationOfTouchInLabel.x - textContainerOffset.x, y: locationOfTouchInLabel.y - textContainerOffset.y)
let indexOfCharacter = layoutManager.characterIndex(for: locationOfTouchInTextContainer, in: textContainer, fractionOfDistanceBetweenInsertionPoints: nil)
return NSLocationInRange(indexOfCharacter, targetRange)
}
}
인식기를 설정하려면 (텍스트와 내용을 채색 한 후에) :
lblTermsOfUse.isUserInteractionEnabled = true
lblTermsOfUse.addGestureRecognizer(UITapGestureRecognizer(target: self, action: #selector(handleTapOnLabel(_:))))
그런 다음 제스처 인식기 :
@objc func handleTapOnLabel(_ recognizer: UITapGestureRecognizer) {
guard let text = lblAgreeToTerms.attributedText?.string else {
return
}
if let range = text.range(of: NSLocalizedString("_onboarding_terms", comment: "terms")),
recognizer.didTapAttributedTextInLabel(label: lblAgreeToTerms, inRange: NSRange(range, in: text)) {
goToTermsAndConditions()
} else if let range = text.range(of: NSLocalizedString("_onboarding_privacy", comment: "privacy")),
recognizer.didTapAttributedTextInLabel(label: lblAgreeToTerms, inRange: NSRange(range, in: text)) {
goToPrivacyPolicy()
}
}