iOS/개념 & 개발

[iOS] UITextView 원하는 문자에만 색상 넣기(여러 개도 OK)

유정주 2022. 9. 4. 17:59
반응형

기능 설명

UITextView의 텍스트에 색상을 넣어야 하는 작업을 해야 했습니다.

모든 글자가 아니라 특정 키워드에만 색상을 적용하는건데요.

이 키워드는 여러 개가 될고 텍스트에 키워드가 여러 번 나올 수 있습니다.

 

예를 하나 들어봅시다.

UITextView에

동해물과 백두산이 마르고 닳도록 하느님이 보우하사 우리나라 만세 무궁화 삼천리 화려강산 대한 사람 대한으로 길이 보전하세 동해물과 백두산이 마르고 닳도록 하느님이 보우하사 우리나라 만세 무궁화 삼천리 화려강산 대한 사람 대한으로 길이 보전하세
...

텍스트가 들어있다고 합시다.

색상을 바꿔야 하는 키워드는 "동해물", "백두산", "하느님" 이라고 할 때,

 

이렇게 되어야 하는거죠.

 

기존 코드의 문제

인터넷에 떠도는 코드로 시도를 해보았습니다.

let attributtedString = NSMutableAttributedString(string: text)
attributtedString.addAttribute(.foregroundColor, value: color, range: (text as NSString).range(of: keyword))

label.attributedText = attributtedString

 

근데 원하는 글자에 강조를 하는 것은 한 번만 적용만 되었습니다.

위 예시 사진처럼 텍스트의 모든 키워드가 강조되어야 하는데, 키워드 당 한 번만 강조가 되었어요.

그래서 앞부분만 강조가 되고 뒷부분은 원본 그대로였죠.

 

NSMutableAttributedString

Text에 원하는 attribute를 추가하기 위해서는 NSMutableAttributedString 클래스를 사용해야 합니다.

텍스트에 대한 속성을 설정할 수 있는 문자열인데 변경 가능하다는 점이 특징입니다.

변경 가능하다는 것은 addAttribute 메서드를 이용해 Attribute를 추가할 수 있다는 의미입니다.

 

비슷한 클래스로는 NSAttributedString가 있습니다.

Mutable이 빠진 것으로 알 수 있듯이 변경이 불가능한 클래스로 addAttribute 메서드를 사용할 수 없습니다.

 

그럼 왜 변경이 가능해야할까? 궁금할 수 있는데요.

저희는 모든 글자에 attribute를 설정하는 것이 아니라 특정 키워드에만 추가해야 합니다.

즉, 일반 스타일의 텍스트에 키워드 부분에 attribute를 추가해야 하기 때문에 NSMutableAttributedString을 사용해야 합니다.

 

기존 코드는 왜 한 번만 적용이 될까?

let attributtedString = NSMutableAttributedString(string: text)
attributtedString.addAttribute(.foregroundColor, value: color, range: (text as NSString).range(of: keyword))

label.attributedText = attributtedString

위에서 봤던 이 코드는 왜 한 번만 적용이 될까요?

 

2번 라인의 range에 집중을 해서 보면 알 수 있는데요.

String의 range(of:)는 키워드의 범위를 리턴해주는 메서드로 전체 text에서 키워드의 범위를 구하는 역할입니다.

이 범위에 foregroundColor를 적용하는 것이죠.

 

그럼 어떻게 개선을 해야할지도 얼추 보이죠?

 

수정 코드

let attrString = NSMutableAttributedString(string: self.text)
        
for keyword in keywords {
    var searchRange = text.startIndex..<text.endIndex
    while let range = text.range(of: keyword, options: .caseInsensitive, range: searchRange) {
        attrString.addAttribute(.foregroundColor, value: color, range: NSRange(range, in: text))

        searchRange = range.upperBound..<searchRange.upperBound
    }
}

keyword의 range를 구해서 foregroundColor를 적용한다는 아이디어는 같습니다.

근데 while문을 이용해 다시 탐색을 하고 적용을 하는거죠.

  1. 처음 searchRange는 text의 처음부터 끝까지 탐색하도록 설정합니다.
  2. keyword가 있는 범위를 searchRange에서 구하고 (caseInsensitive 옵션은 대소문자 구분을 무시)
  3. addAttribute의 range는 NSRange 타입을 전달해야하기 때문에 원본 text에서 range만큼의 NSRange를 생성해서 전달합니다.
  4. 범위를 찾으면 그 범위에 foregroundColor 옵션을 설정합니다.
  5. 마지막으로 searchRange를 찾은 범위 이후 ~ 끝으로 다시 설정합니다.

 

이러면 정상적으로 모든 글에 키워드가 강조되는 것을 볼 수 있습니다~


아직은 초보 개발자입니다.

더 효율적인 코드 훈수 환영합니다!

공감 댓글 부탁드립니다.

 

 

 

반응형