🧑‍💻/Swift

Swift UILabel 텍스트 부분 색상/폰트/볼드/밑줄 적용하기

유리맥 2023. 3. 1. 18:30
반응형

개발하다 보면 특정 부분에 볼드되거나 색상 변경, 밑줄이 들어가는 경우가 종종 있습니다.

공통적으로 사용할 수 있기 때문에 UILabel을 extension하여 구현해 두면 요긴하게 쓸 수 있죠!

위 예시를 보면 "지날 달 보러가기" 는 밑줄을,
"감정 카드 저장을 원하시면 설정에서 사진 접근을 허용하세요." 는 부분 볼드 처리를 해야합니다.

 

각 함수를 구현하기 전 개념을 짚고 넘어가 봅시다~

 

개념 이해하기

부분 적용을 위해서는 UILabel의 attributedText를 설정해야합니다.
아래 코드는 UIKit.UILabel을 발췌했습니다.
attributedText의 주석을 보시면 'attributedText 지정 시 text, font, textColor 등 모든 속성이 무시된다' 고 하네요.

import Foundation

//
//  UILabel.h
//  UIKit
//
//  Copyright (c) 2006-2018 Apple Inc. All rights reserved.
//

@available(iOS 2.0, *)
@MainActor open class UILabel : UIView, NSCoding, UIContentSizeCategoryAdjusting {

    
    open var text: String? // default is nil

    open var font: UIFont! // default is nil (system font 17 plain)

    open var textColor: UIColor! // default is labelColor

    open var shadowColor: UIColor? // default is nil (no shadow)

    open var shadowOffset: CGSize // default is CGSizeMake(0, -1) -- a top shadow

    open var textAlignment: NSTextAlignment // default is NSTextAlignmentNatural (before iOS 9, the default was NSTextAlignmentLeft)

    open var lineBreakMode: NSLineBreakMode // default is NSLineBreakByTruncatingTail. used for single and multiple lines of text

    
    // the underlying attributed string drawn by the label, if set, the label ignores the properties above.
    @available(iOS 6.0, *)
    @NSCopying open var attributedText: NSAttributedString? // default is nil

 

 

UILabel text를 AttributedString으로 변환하기

그렇다면 attributedText를 지정하면 그 전에 내가 설정한 모든 속성 값이 초기화 된다는 말인데...
초기화 되는 문제를 미연에 방지하기 위해
내가 설정한 text와 font를 attributedString으로 바꾸어 주는 함수를 추가했습니다.

import UIKit

extension UILabel {
    /* AttributedString이 설정되어있지 않으면 생성하여 반환한다. */
    private func mutableAttributedString() -> NSMutableAttributedString? {
        guard let labelText = self.text, let labelFont = self.font else { return nil }
        
        var attributedString: NSMutableAttributedString?
        if let attributedText = self.attributedText {
            attributedString = attributedText.mutableCopy() as? NSMutableAttributedString
        } else {
            attributedString = NSMutableAttributedString(string: labelText,
                                                         attributes: [NSAttributedString.Key.font :labelFont])
        }
        
        return attributedString
    }
}

더 많은 NSAttributedString.key를 보고 싶다면 공식 문서를 참고해주세요.

 

 

 

UILabel 특정 부분 Color 지정하기

color 지정은 UILabel.textColor로 지정할 수 있지만 부분 컬러를 지정하기 위해 NSRange 타입을 parameter로 받았습니다.
먼저 attributedString으로 변환한 뒤 NSAttributedString.key.foregroundColor를 해당 범위만큼만 다른 색상으로 대체합니다.

extension UILabel {
    /* 텍스트 구간 색상 변경 */
    func setTextColor(_ color: UIColor, range: NSRange) {
        guard let attributedString = self.mutableAttributedString() else { return }
        
        attributedString.addAttribute(NSAttributedString.Key.foregroundColor, value: color, range: range)
        self.attributedText = attributedString
    }
    
    /* AttributedString이 설정되어있지 않으면 생성하여 반환한다. */
    private func mutableAttributedString() -> NSMutableAttributedString? {
        guard let labelText = self.text, let labelFont = self.font else { return nil }
        
        var attributedString: NSMutableAttributedString?
        if let attributedText = self.attributedText {
            attributedString = attributedText.mutableCopy() as? NSMutableAttributedString
        } else {
            attributedString = NSMutableAttributedString(string: labelText,
                                                         attributes: [NSAttributedString.Key.font :labelFont])
        }
        
        return attributedString
    }
}

 

 

 

UILabel 특정 부분 Font 지정하기

Color 지정과 유사하게, NSAttributedString.key.font를 적용합니다.

extension UILabel {    
    /* 텍스트 구간 폰트 변경 */
    func setFont(_ font: UIFont, range: NSRange) {
        guard let attributedString = self.mutableAttributedString() else { return }
        
        attributedString.addAttribute(NSAttributedString.Key.font, value: font, range: range)
        self.attributedText = attributedString
    }
    
    /* 텍스트 구간 볼드 폰트 변경 */
    func setBoldFont(_ boldFontName: String, range: NSRange) {
        guard let font = self.font,
              let boldFont = UIFont(name: boldFontName, size: font.pointSize) else {
            return
        }
        
        return setFont(boldFont, range: range)
    }
    
    /* AttributedString이 설정되어있지 않으면 생성하여 반환한다. */
    private func mutableAttributedString() -> NSMutableAttributedString? {
        guard let labelText = self.text, let labelFont = self.font else { return nil }
        
        var attributedString: NSMutableAttributedString?
        if let attributedText = self.attributedText {
            attributedString = attributedText.mutableCopy() as? NSMutableAttributedString
        } else {
            attributedString = NSMutableAttributedString(string: labelText,
                                                         attributes: [NSAttributedString.Key.font :labelFont])
        }
        
        return attributedString
    }
}

 

 

 

UILabel 특정 부분 밑줄(Underline) 지정하기

NSAttributedString.key.underlineStyle로 밑줄을 적용합니다.
NSUnderlineStyle의 rawValue (int)를 넣어주면 됩니다.

extension UILabel {    
    /* 밑줄 추가 */
    func setUnderline(range: NSRange) {
        guard let attributedString = self.mutableAttributedString() else { return }
        
        attributedString.addAttribute(NSAttributedString.Key.underlineStyle, value: NSUnderlineStyle.single.rawValue, range: range)
        self.attributedText = attributedString
    }
    
    /* AttributedString이 설정되어있지 않으면 생성하여 반환한다. */
    private func mutableAttributedString() -> NSMutableAttributedString? {
        guard let labelText = self.text, let labelFont = self.font else { return nil }
        
        var attributedString: NSMutableAttributedString?
        if let attributedText = self.attributedText {
            attributedString = attributedText.mutableCopy() as? NSMutableAttributedString
        } else {
            attributedString = NSMutableAttributedString(string: labelText,
                                                         attributes: [NSAttributedString.Key.font :labelFont])
        }
        
        return attributedString
    }
}

사용할 때는 UIButton의 titleLabel인 경우 아래와 같이 전체 밑줄을 줄 수 있습니다.

btnLastMonth.titleLabel?.setUnderline(range: NSRange(location: 0, length: btnLastMonth.currentTitle?.count ?? 0))

 

 

 

전체 코드

import UIKit

extension UILabel {
    /* 텍스트 구간 색상 변경 */
    func setTextColor(_ color: UIColor, range: NSRange) {
        guard let attributedString = self.mutableAttributedString() else { return }
        
        attributedString.addAttribute(NSAttributedString.Key.foregroundColor, value: color, range: range)
        self.attributedText = attributedString
    }
    
    /* 텍스트 구간 볼드 폰트 변경 */
    func setBoldFont(_ boldFontName: String, range: NSRange) {
        guard let font = self.font,
              let boldFont = UIFont(name: boldFontName, size: font.pointSize) else {
            return
        }
        
        return setFont(boldFont, range: range)
    }
    
    /* 텍스트 구간 폰트 변경 */
    func setFont(_ font: UIFont, range: NSRange) {
        guard let attributedString = self.mutableAttributedString() else { return }
        
        attributedString.addAttribute(NSAttributedString.Key.font, value: font, range: range)
        self.attributedText = attributedString
    }
    
    /* 밑줄 추가 */
    func setUnderline(range: NSRange) {
        guard let attributedString = self.mutableAttributedString() else { return }
        
        attributedString.addAttribute(NSAttributedString.Key.underlineStyle, value: NSUnderlineStyle.single.rawValue, range: range)
        self.attributedText = attributedString
    }
    
    /* AttributedString이 설정되어있지 않으면 생성하여 반환한다. */
    private func mutableAttributedString() -> NSMutableAttributedString? {
        guard let labelText = self.text, let labelFont = self.font else { return nil }
        
        var attributedString: NSMutableAttributedString?
        if let attributedText = self.attributedText {
            attributedString = attributedText.mutableCopy() as? NSMutableAttributedString
        } else {
            attributedString = NSMutableAttributedString(string: labelText,
                                                         attributes: [NSAttributedString.Key.font :labelFont])
        }
        
        return attributedString
    }
}

 

 

출처 : iOS App MyLittleUniverse

반응형