현재 저는 맥덕 iOS앱을 개발중입니다.

맥덕 앱에 대해선 추후에 런칭이 끝난 뒤 제대로 소개하겠습니다.


 

맥덕 앱의 검색기능을 구현 중인데,

검색 시 일치하는 키워드색상이 변경하는 걸 많이 보셨을 겁니다.

(기억이 잘 안나신다면 아래 이미지를 참고하세요.)

일치하는 키워드 색상 변경 - 네이버 검색창

 


 

우선 일치하는 키워드 변경 기능을 완성한 모습부터 보여드리겠습니다.

일치하는 키워드는 색상, 폰트(bold)가 변경됩니다.

 

일치하는 키워드 색상, 폰트 변경.

 


 

위 기능은 크게 3가지의 조건 합쳐져 만들어졌습니다.

 

1. 텍스트의 특정 부분만 색상과 폰트를 변경하는 것. - color, font를 변경하는 거 자체는 쉬웠습니다. (응용한 방법은 아래 설명에 있습니다.)

 

2. 문자열에서 원하는 문자(열)의 인덱스를 찾아야 하는 것. - 1번의 특정 부분의 인덱스를 찾아야 하기 때문에, 필수적인 조건인데 이 부분에서 꽤 오랜 시간동안 막혔습니다. 다행히 여러 레퍼런스를 참조한 뒤, 그것들을 한데 모아 해결했습니다.

 

3. 폰트 bold 처리 작업 시 기존 사이즈의 변경 없이 처리해야 하는 것. - 스택오버플로우의 명쾌한 답변으로 해결했습니다.

 


 

코드를 설명드리기 전에 전체적인 구조는 다음과 같습니다.

 

코드의 전체적인 구조 - 조건 1, 2, 3

 


 

조건 0. 빨간색과 하늘색 박스의 바깥 부분은 검색 테이블뷰 입니다. 이번 설명과 무관합니다.


해결 방법

 

조건 1 해결 방법 (텍스트의 특정 부분만 색상과 폰트를 변경하는 것.)

우선 https://icksw.tistory.com/152 를 참고합니다.

 

저는 이미 있는 특정 텍스트의 색상을 변경하는 것이 아닌,

[서버에서 텍스트를 받아온 것 <-> 사용자가 검색한 텍스트]를 비교해야 하기 때문에,

range부분이 특정 텍스트(ex: "제주")가 아닌 범위로 필요합니다.

(+ 22/01/04 추가 사항 : 작업하면서 알게 된 내용 추가 작성합니다.

범위가 필요하더라도 인덱스 범위를 구할 필요 없이,

range: (text as NSString).range(of: "\(searchBar.text!)") 이런식으로 하시면 아래 설명보다 더욱 간단히 해결할 수 있었습니다.

만약 이 추가 방법으로 진행하신다면, 영어 대소문자 구분해주는 코드가 추가적으로 필요합니다. )

그러기 위해선 사용자가 검색한 텍스트서버에서 받아온 텍스트어느 인덱스에 위치하는지 알아야 기능을 구현할 수 있게 됩니다.

(+ 22/01/04 추가 사항 : 꼭 인덱스를 모르더라도 위 추가사항 방법처럼 해결할 수도 있습니다.)

 

어느 인덱스에 위치하는지 아는 방법조건 2를 통해 해결할 수 있습니다.

 

특정 텍스트 예 | range: (text as NSString).range(of: "제주")

범위 예 | range: NSRange(location: textFirstIndex, length: searchingKeyword.count)

 

let attributeString = NSMutableAttributedString(string: text) // 텍스트 일부분 색상, 폰트 변경

attributeString.addAttribute(.foregroundColor, value: UIColor.subYellow, range: NSRange(location: textFirstIndex, length: searchingKeyword.count)) // 텍스트 색상(yellow) 변경.
attributeString.addAttribute(.font, value: UIFont.boldSystemFont(ofSize: cell.beerName.font.pointSize), range: NSRange(location: textFirstIndex, length: searchingKeyword.count))

(최하단에 위치한 전체 코드를 참고하세요.)


조건 2 해결 방법 (문자열에서 원하는 문자(열)의 인덱스를 찾아야 하는 것.)

 

우선 사용자가 검색한 텍스트(찾고자 하는 문자열)가 없을 수 있으니 if문을 사용합니다.

 

text.range(of: ) 를 통해 사용자가 검색한 텍스트가  서버에서 받아온 텍스트의 어느 범위에 있는지 알 수 있습니다.

options에서 .caseInsensitive를 통해 대소문자 구분을 하지 않도록 했습니다.

(.backwards : 뒤에서부터 문자열을 검색한 뒤, 처음으로 찾은 문자열의 인덱스를 리턴합니다.)

 

그 다음 text.distance(from: , to: ) 메소드를 통해,

from 문자열의 시작점(0)부터 - to 사용자가 검색한 텍스트의 첫 인덱스까지의 거리(=인덱스)를 구해 저장합니다.

 

조건 1에서 필요했던 인덱스를 위 방법을 통해 구했으니,

첫 인덱스부터 ~ 사용자가 검색한 텍스트의 크기까지 색상과 폰트를 변경합니다.

range: NSRange(location: textFirstIndex, length: searchingKeyword.count)

 

var textFirstIndex: Int = 0 // 검색중인 키워드가 가장 처음으로 나온 인덱스를 저장할 변수 선언.
            if let textFirstRange = text.range(of: "\(searchingKeyword)", options: .caseInsensitive) { // 검색중인 키워드가 있을 때에만 색상 변경 - 검색중인 키워드가 가장 처음으로 일치하는 문자열의 범위를 알아낼 수 있음. (caseInsensitive:대소문자 구분X)
                textFirstIndex = text.distance(from: text.startIndex, to: textFirstRange.lowerBound) // 거리(인덱스) 구해서 저장.
                
                attributeString.addAttribute(.foregroundColor, value: UIColor.subYellow, range: NSRange(location: textFirstIndex, length: searchingKeyword.count)) // 텍스트 색상(yellow) 변경.
                attributeString.addAttribute(.font, value: UIFont.boldSystemFont(ofSize: cell.beerName.font.pointSize), range: NSRange(location: textFirstIndex, length: searchingKeyword.count)) // 기존 사이즈 변경 없이 bold처리 : https://stackoverflow.com/questions/39999093/swift-programmatically-make-uilabel-bold-without-changing-its-size
                cell.beerName.attributedText = attributeString // ex) "제주" 위트 에일(JEJU Wit ale)
                cell.selectionStyle = .none // 테이블뷰 cell 선택시 배경색상 없애기 : https://gonslab.tistory.com/41 | https://sweetdev.tistory.com/105
            }

구현 참고 자료 : t.ly/9bVA | t.ly/ZUfj

NSRange 참고 자료 : https://youtu.be/TkzdnzYxZbo?t=424

 


조건 3 해결 방법 (폰트 bold 처리 작업 시 기존 사이즈의 변경 없이 처리해야 하는 것.)

 

 

.boldSystemFont를 통해 bold처리를 한 뒤, ofSize: 에서 기존 사이즈를 넣어주면 됩니다.

스택오버플로우 자료를 통해 간단히 해결 할 수 있었습니다.

 

attributeString.addAttribute(.font, value: UIFont.boldSystemFont(ofSize: cell.beerName.font.pointSize), range: NSRange(location: textFirstIndex, length: searchingKeyword.count)) // 기존 사이즈 변경 없이 bold처리 : https://stackoverflow.com/questions/39999093/swift-programmatically-make-uilabel-bold-without-changing-its-size

구현 참고 자료 : https://stackoverflow.com/questions/39999093/swift-programmatically-make-uilabel-bold-without-changing-its-size

 


지금까지 구현 방법에 대해서 설명드렸습니다.

검색 시 일치하는 키워드 색상 및 폰트 변경 기능 구현에 살짝 애를 먹었지만, 결국엔 해결했습니다!

제가 설명드린 방법대로 본인 프로젝트에 적용하신다면 충분히 해결될 겁니다.

작업 중 막히시거나, 질문이 있으시다면 댓글을 통해 편하게 질문주세요.

도움이 되셨다면 좋겠습니다!


 

아래는 결과 화면에 대한 전체 코드입니다.

 

전체 코드

else if tableView == searchingTableView { // 검색중 테이블뷰 일 때
            let cell = tableView.dequeueReusableCell(withIdentifier: SearchingTableViewCell.identifier, for: indexPath) as! SearchingTableViewCell
            
            let searchingModel: SearchingModel = SearchingList[indexPath.row]
            
            let text: String = "\(searchingModel.beerNameKr)(\(searchingModel.beerNameEn))" // ex) 제주 위트 에일(JEJU Wit ale)
            let attributeString = NSMutableAttributedString(string: text) // 텍스트 일부분 색상, 폰트 변경 - https://icksw.tistory.com/152
            // 문자열에서 원하는 문자의 인덱스 찾는 방법 - t.ly/ci4z
            var textFirstIndex: Int = 0 // 검색중인 키워드가 가장 처음으로 나온 인덱스를 저장할 변수 선언.
            if let textFirstRange = text.range(of: "\(searchingKeyword)", options: .caseInsensitive) { // 검색중인 키워드가 있을 때에만 색상 변경 - 검색중인 키워드가 가장 처음으로 일치하는 문자열의 범위를 알아낼 수 있음. (caseInsensitive:대소문자 구분X)
                textFirstIndex = text.distance(from: text.startIndex, to: textFirstRange.lowerBound) // 거리(인덱스) 구해서 저장.
                
                attributeString.addAttribute(.foregroundColor, value: UIColor.subYellow, range: NSRange(location: textFirstIndex, length: searchingKeyword.count)) // 텍스트 색상(yellow) 변경.
                attributeString.addAttribute(.font, value: UIFont.boldSystemFont(ofSize: cell.beerName.font.pointSize), range: NSRange(location: textFirstIndex, length: searchingKeyword.count)) // 기존 사이즈 변경 없이 bold처리 : https://stackoverflow.com/questions/39999093/swift-programmatically-make-uilabel-bold-without-changing-its-size
                cell.beerName.attributedText = attributeString // ex) "제주" 위트 에일(JEJU Wit ale)
                cell.selectionStyle = .none // 테이블뷰 cell 선택시 배경색상 없애기 : https://gonslab.tistory.com/41 | https://sweetdev.tistory.com/105
            }
            
            return cell
}

 

 

 

반응형