삽질기

[iOS] Swfit로 해시태그 기능 구현하기

e.den 2021. 3. 25. 17:21

진행 중이던 프로젝트에서 해시태그 기능을 작업했다.

일단 내가 원하는 기능은 해시태그끼리 붙여놔도 잘 작동되기, 한글 인식하기 정도였는데 마침 어떤 개발자 분의 블로그에 좋은 코드가 있어서 참고를 했다.

 

import UIKit

// 한글, 영문, 숫자만 가능
class HashtagTextView: UITextView {
    var hashtagArr: [String]?
    
    func resolveHashTags() {
        self.isEditable = false
        self.isSelectable = true
        
        let nsText: NSString = self.text as NSString
        let attrString = NSMutableAttributedString(string: nsText as String)
        let hashtagDetector = try? NSRegularExpression(pattern: "#(\\w+)", options: NSRegularExpression.Options.caseInsensitive)
        let results = hashtagDetector?.matches(in: self.text,
                                               options: NSRegularExpression.MatchingOptions.withoutAnchoringBounds,
                                               range: NSMakeRange(0, self.text.utf16.count))

        hashtagArr = results?.map{ (self.text as NSString).substring(with: $0.range(at: 1)) }
                                
        if hashtagArr?.count != 0 {
            var i = 0
            for var word in hashtagArr! {
                word = "#" + word
                if word.hasPrefix("#") {
                    let matchRange:NSRange = nsText.range(of: word as String, options: .caseInsensitive)
                                                                
                    attrString.addAttribute(NSAttributedString.Key.link, value: "\(i)", range: matchRange)
                    i += 1
                }
            }
        }

        self.attributedText = attrString
    }
}

 

간단하게 코드 설명을 하자면 글 내용에서 #으로 시작하는 글자가 있을 때, # 단위로 끊어서 한글 인식이 되도록 인코딩을 해주었고 인코딩 된 단어를 배열에 집어넣었다.

그리고 반복문을 돌려서 글 내용에 배열에 넣었던 단어가 포함되어 있을 경우

addAttribute(NSAttributedString.Key.link, value:, range:)를 써서 태그 된 단어에 링크 속성이 적용되도록 만들었다.

 

결과1

한글 인식도 잘 되고

결과2

해시태그끼리 붙여서 써도 아주 잘 된다

 

그런데... 발생한 문제

중복된 글자는 링크 처리가 안 되는 문제

중복된 글자는 링크 처리가 안 된다...

그래서 어떤 부분이 문제인가 봤더니

 

let matchRange:NSRange = nsText.range(of: word as String, options: .caseInsensitive)
                                                                
attrString.addAttribute(NSAttributedString.Key.link, value: "\(i)", range: matchRange)

 

range 함수 옵션 부분을 보면 caseInsensitive라고 되어있다.

이 옵션은 텍스트뷰 안에서 대소문자를 구분하지 않고, 맨 처음부터(중요함) 일치하는 단어가 있는지 찾는 옵션이다.

앞에서 #한글이라는 단어를 찾을 때 이미 첫 부분에 #한글로 라는 태그가 있기 때문에 이걸로 인식해버리는 것...

 

그래서 옵션을 backwards(맨 뒤에서부터 검색하는 옵션) 이걸로 바꿔보기도 하며(그래도 안 됐다) 수많은 삽질을 반복했고

결론은... 성공했다!

찾아보니까 옵션을 배열로 처리해서 두 가지 속성을 집어넣는 방법이 있었다.

 

코드는 이렇게 수정했다. 앞에서부터 찾고, 뒤에서부터도 찾는 방식

 

let matchRange:NSRange = nsText.range(of: word as String, options: [.caseInsensitive, .backwards])

 

 

결과3

이렇게 잘 나오는 걸 확인할 수 있다!

참고한 사이트는 밑에

 

pythonq.com/so/ios/494801

 

ios - UITextView가 해시 태그를 감지하도록하는 방법? - IT 툴 넷

ios - UITextView가 해시 태그를 감지하도록하는 방법? 출처 ios swift uitextview

pythonq.com

azsha.tistory.com/56

 

[Swift] TextView 안에 있는 Hashtag 텍스트를 Link 하기

extension UITextView { func resolveHashTags(){ let text:String = self.text let nsText:NSString = self.text let words:[NSString] = text.componentsSeparatedByStrings(["\n", " "])   let attrs = [ NSFo..

azsha.tistory.com