NSAttributedString이요?

shintwl·2024년 3월 13일
1

강의를 듣다가 NSAttributedString의 존재를 알게 되었다

(이 강의 개인적으로 추천. 단, Rx와 MVVM에 대한 이해가 있어야 함)

NSAttributeString이 뭐죠?

문자열의 일부분에 다른 스타일을 적용할 수 있는 String이라고 생각하면 된다

  • NSMutableAttributedString
  • NSAttributedString

두가지가 있는데

Attribute(스타일)를 객체 생성 후에 추가 할 수 있는지 아닌지에 따라 선택하면 된다

사용법

NSMutableAttributedString(string:attributes:)로 객체 생성을 한 후
해당 객체에 addAttributes(_ attrs:range:)를 통해 다른 스타일과 그 스타일을 적용할 범위를 지정하면 된다

그 후 UITextView 등 text를 표시할 수 있는 View의 attributedText로 할당해주면 완료

예시

이미지

전체 코드

import UIKit

let sampleString = "문장의 단어를 강조해 봅시다"

class ViewController: UIViewController {
    private var textView: UITextView = {
        let view = UITextView()
        // 여기 주목 👹👹👹👹
        let text = NSMutableAttributedString(
            string: sampleString,
            attributes: [.font : UIFont.systemFont(ofSize: 16)])
        let loc = sampleString.index(of: "강조")!.distance(in: sampleString) // 문장 내에 있는 "강조" 문자열을 찾기
        text.addAttributes([
            .font: UIFont.systemFont(ofSize: 24, weight: .bold),
            .foregroundColor: UIColor.systemRed
        ], range: NSMakeRange(loc, 2))
        view.attributedText = text
        return view
    }()

    override func viewDidLoad() {
        super.viewDidLoad()
        layout()
    }
    
    private func layout() {
        view.addSubview(textView)
        textView.translatesAutoresizingMaskIntoConstraints = false
        NSLayoutConstraint.activate([
            textView.widthAnchor.constraint(equalTo: view.safeAreaLayoutGuide.widthAnchor, multiplier: 0.5),
            textView.heightAnchor.constraint(equalTo: view.safeAreaLayoutGuide.heightAnchor, multiplier: 0.5),
            textView.centerXAnchor.constraint(equalTo: view.safeAreaLayoutGuide.centerXAnchor),
            textView.centerYAnchor.constraint(equalTo: view.safeAreaLayoutGuide.centerYAnchor)
        ])
    }
}

// indexing을 간결하게 하기 위해 추가
// from: https://stackoverflow.com/questions/32305891/index-of-a-substring-in-a-string-with-swift
extension StringProtocol {
    func index<S: StringProtocol>(of string: S, options: String.CompareOptions = []) -> Index? {
        range(of: string, options: options)?.lowerBound
    }
    func endIndex<S: StringProtocol>(of string: S, options: String.CompareOptions = []) -> Index? {
        range(of: string, options: options)?.upperBound
    }
    func indices<S: StringProtocol>(of string: S, options: String.CompareOptions = []) -> [Index] {
        ranges(of: string, options: options).map(\.lowerBound)
    }
    func ranges<S: StringProtocol>(of string: S, options: String.CompareOptions = []) -> [Range<Index>] {
        var result: [Range<Index>] = []
        var startIndex = self.startIndex
        while startIndex < endIndex,
            let range = self[startIndex...]
                .range(of: string, options: options) {
                result.append(range)
                startIndex = range.lowerBound < range.upperBound ? range.upperBound :
                    index(range.lowerBound, offsetBy: 1, limitedBy: endIndex) ?? endIndex
        }
        return result
    }
}

// from: https://stackoverflow.com/questions/34540185/how-to-convert-index-to-type-int-in-swift
extension StringProtocol {
    func distance(of element: Element) -> Int? { firstIndex(of: element)?.distance(in: self) }
    func distance<S: StringProtocol>(of string: S) -> Int? { range(of: string)?.lowerBound.distance(in: self) }
}

extension Collection {
    func distance(to index: Index) -> Int { distance(from: startIndex, to: index) }
}

extension String.Index {
    func distance<S: StringProtocol>(in string: S) -> Int { string.distance(to: self) }
}

참고

https://developer.apple.com/documentation/foundation/nsattributedstring
https://developer.apple.com/documentation/foundation/nsmutableattributedstring
https://stackoverflow.com/questions/32305891/index-of-a-substring-in-a-string-with-swift
https://stackoverflow.com/questions/34540185/how-to-convert-index-to-type-int-in-swift

1개의 댓글

comment-user-thumbnail
2024년 3월 14일

특정 문자열에 변화를 주고 싶을 때 몇번 사용해본 경험이 있는 것 같아 반가웠습니다ㅎㅎ

iOS 15이상부터는 AttributedString을 지원하더라구요! SwiftUI에서 NSAttributedString을 사용하기 위해 여러번 꼬아서 엮거나 텍스트를 각각으로 만들어 이어붙이거나 했던 것 같은데 이제 간편히 Text(AttributedString)만 넣어도 되서 편리해진 것 같아요.

또 앱 타겟버전에 따라 분기처리를 하면 너무 유용할 것 같아요! 😊

답글 달기