[iOS] UILabel - 현재 적용된 Line 수 구하기

2021. 9. 13. 00:20iOS

반응형

오늘은 회사에서 개발을 진행하면서 UX적으로 UILabel의 Line이 3줄을 넘어갈 경우, 특정 부분을 ...으로 처리해주는 요구사항이 있었어서 해당 부분을 해결하기 위해 사용했던 방법에 대해 알아보려고 합니다.

 

이 요구사항을 구현하기 위해 많은 삽질(?)들을 했었는데요.

 

처음엔 아... 한 글자마다 width가 다르기 때문에, 한 글자의 크기를 구할 수 있다면 최대 라인에서 넘어간 글자만큼의 크기를 구해서 그정도의 크기에 맞게 해당 문자를 ...으로 처리해주어야겠다 생각해서 삽질을 시작했습니다..  (i, ㅁ의 넓이가 다르잖아요..? 그래서 각 넓이를 구해야겠다 생각했습니다.)

 

하지만 이 방법은 아닌것 같다고 생각이 들었고, 구글링을 해보아도 한 글자의 넓이를 구하는 부분은 나오지 않는 것 같더라구요 😂

 

다음으로 생각해낸 방법이 오늘 포스팅을 할 주제입니다.

바로 현재 Label의 Line 수를 구해서 3줄을 넘은 경우, 마지막 라인에서 글자수를 줄여가며 Line이 3줄 안으로 들어오는지 확인하는 방법이었습니다. 물론 o(n)의 시간 복잡도를 가지겠지만, 괜찮은 방법이라고 생각이 들었고 결국 해결해서 반영했습니다 👍

 

그렇다면 UIKit UILabel에서는 Max Line을 지정하는 numberOfLines 프로퍼티 외에는 제공하지 않는 것으로 알고 있는데요. 어떻게 UILabel의 현재 Line을 구했는지 알아보겠습니다.

 

 

UILabel  현재 Line 수 구하기

우선 해당 요구사항을 반영하기 위해, 핵심적으로 사용했던 메소드에 대해 알아보겠습니다. 바로 NSStringboundingRect라는 녀석입니다.

Apple에서 설명하는 부분을 보면 다음과 같이 되어 있는데요.

옵션 값과 같이 넣어준 String을 그리기 위해 특정한 사각형 안에서 필요한 경계 사각형의 크기를 리턴해준다고 되어 있는 것 같습니다. 또한 현재 사진에서 외에 밑의 문서를 더 읽어보면, 여러 Single Line이 아닌 Multi-Line에서의 정확한 크기를 알고 싶으면, usesLineFragmentOrigin Option을 이용하라고 되어 있습니다. 그리고 나오는 값을 ceil 메소드를 이용해서 꼭 근방의 Integer 값을 이용하라고 당부되어 있습니다.

 

그렇다면, 이 메소드를 이용해서 어떻게 현재 UILabel의 Line 수를 구할지 감이 오시나요..?

 

우선, UILabel의 Extension에 구현해서 지속적으로 사용할 수 있게 했습니다.

바로 코드로 알아볼게요~

extension UILabel {

    func countCurrentLines() -> Int {
        guard let text = self.text as NSString? else { return 0 }
        guard let font = self.font              else { return 0 }
        
        var attributes = [NSAttributedString.Key: Any]()
        
        // kern을 설정하면 자간 간격이 조정되기 때문에, 크기에 영향을 미칠 수 있습니다.
        if let kernAttribute = self.attributedText?.attributes(at: 0, effectiveRange: nil).first(where: { key, _ in
            return key == .kern
        }) {
            attributes[.kern] = kernAttribute.value
        }
        attributes[.font] = font
        
        // width을 제한한 상태에서 해당 Text의 Height를 구하기 위해 boundingRect 사용
        let labelTextSize = text.boundingRect(
            with: CGSize(width: self.bounds.width, height: .greatestFiniteMagnitude),
            options: .usesLineFragmentOrigin,
            attributes: attributes,
            context: nil
        )
        
        // 총 Height에서 한 줄의 Line Height를 나누면 현재 총 Line 수
        return Int(ceil(labelTextSize.height / font.lineHeight))
	} 
 
}

이렇게 바로 현재의 총 Line 수를 간단하게 구할 수 있는데요. 

 

저는 이 메소드를 이용해서 제일 위에서 설명했던 기능을 구현했습니다 🙃

 

총 3줄이 넘어갈 경우, 모든 Text를 표현하기 위해 유저의 이름을 ...으로 생략하는 UX적인 요구였는데요. 3줄이 넘어가는 경우 유저의 이름에서 ...으로 하나씩 처리하며 총 3줄 안으로 들어오는 지 확인해서 적용하는 방향으로 해결했습니다

 

필요한 일이 있다면 유용하게 사용했으면 좋겠네요 :)

반응형