최근에 위와 같이 멘션을 포함한 대댓글을 구현해야했는데
어떻게 해야할 지 전혀 감을 못잡았었기에 복습 및 공유를 위해 작성합니다 ..😅
이 방법을 시도하려다 갑자기 깨달았습니다 사용자는 아이디 언급을 댓글 상단, 중간, 하단 어디서나 할 수 이있다는 것을..(아..🌟
그래서 어떤 방법이 있을 지 서치를 하다가 해시태그 구현에 대한 포스팅을 보게 되었습니다.
그래서 오! 그렇다면 언급도 해시태그처럼 구현하면 되지 않을까? 라고 생각하며 적용해보았습니다.
link란?
text에 적용하는 NSAttributed.key 의 속성 중 하나로 text에 NSURL 값을 넣을 수 있고 해당 text를 누르면 해당 URL을 전달 받을 수 있음
따라서, 이 link 속성을 이용하여 해당 text(언급된 아이디)를 버튼처럼 clickable하게 만들어준다면 우리가 원하는 댓글 내 언급 기능을 구현할 수 있습니다. 🌟
이제 이 link속성을 가진 TextView(MentionTextView)를 만들어 보겠습니다.
import UIKit
/// 멘션된 아이디를 clickable하게 바꿔주는 텍스트 뷰
/// 한글, 영문, 숫자만 가능
class MentionTextView: UITextView {
var idArray: [String] = []
func findOutMetionedId() {
self.isEditable = false
self.isSelectable = true
self.isScrollEnabled = false
/// mentionTextView의 텍스트에 기본으로 적용되는 설정 값
let attributes: [NSAttributedString.Key: Any] = [
.foregroundColor: UIColor.black,
.font: UIFont.systemFont(ofSize: 14.0, weight: .regular),
]
let nsText: NSString = self.text as NSString
let attrString = NSMutableAttributedString(string: nsText as String, attributes: attributes)
let mentionDetector = try? NSRegularExpression(pattern: "@(\\w+)", options: NSRegularExpression.Options.caseInsensitive)
let results = mentionDetector?.matches(in: self.text,
options: NSRegularExpression.MatchingOptions.withoutAnchoringBounds,
range: NSMakeRange(0, self.text.utf16.count))
guard let results = results else { return }
idArray = results.map{ (self.text as NSString).substring(with: $0.range(at: 1)) }
if !idArray.isEmpty {
var i = 0
for var word in idArray {
word = "@" + word
if word.hasPrefix("@") {
let matchRange:NSRange = nsText.range(of: word as String, options: [.caseInsensitive, .backwards])
/// @로 시작하는 텍스트인 경우의 속성
let linkAttributes: [NSAttributedString.Key: Any] = [
.foregroundColor: UIColor(named: "Primary") ?? .black,
.font: UIFont.systemFont(ofSize: 14.0, weight: .regular),
.link: "\(i)",
]
attrString.setAttributes(linkAttributes, range: matchRange)
i += 1
}
}
}
self.attributedText = attrString
}
}
여기서 주의해서 설정해야하는 건 linkAttributes 입니다.
이제 mentionTextView내에서 언급된 아이디를 눌렀을 때 해당 아이디를 print하는 action을 구현해보겠습니다.
textView 내의 url과 사용자 간의 상호작용을 허용할 것인지 묻는 메서드
extension CommentView: UITextViewDelegate {
func textView(_ textView: UITextView, shouldInteractWith url: URL, in characterRange: NSRange, interaction: UITextItemInteraction) -> Bool {
if let mentionTextView = textView as? MentionTextView,
let text = Int(url.debugDescription) {
print(mentionTextView.idArray[text])
return true
}
return false
}
}
따라서 url 값을 가지고 있는 clickable 한 id를 눌렀을 때 호출됩니다.
아이디 print action이 UITextViewDelegate 메서드에서 수행되기 때문에 반드시 delegate 를 설정해주어야합니다.
textView에 텍스트를 넣어주고난 후에 findOutMetionedId() 호출해야합니다.
textView의 text 속성은 외부에서 변경해도 적용되지 않음
Label도 되긴 했는데...
그러나 custom에 제약이 있었습니다. 모든 속성을 테스트해본건 아니지만, .foregroundColor는 확실히 적용되지 않았습니다 🥹
저는 프로젝트의 Primary color로 언급된 아이디 색상도 바뀌어야했기에 textView로 구현하였으나
만약 default blue로 구현하실 예정이시라면 Label로 구현하셔도 괜찮을 것 같습니다.