String이 있는데 왜 AttributedString을 사용하는 걸까?
Swift에는 String이 있는데 왜 attributedString을 사용할까요?(attributedString이란 NSAttributedString, NSMUtableAttributedString으로부터 init된 string을 의미) AttributedString은 attributes 추가를 통해 format이 가능하기 때문입니다. attributes는 text의 font, kerning, backgroundColor, foregroundColor, shadow 등 NSAttributedString.Key를 통해 설정할 수 있는 것들을 의미합니다.
UILabel을 예로 들자면, 그림1과 같이 text, attributedText 두개의 변수를 옵셔널 타입으로 갖고 있습니다. String 타입의 text를 사용한 후에 text에 변화를 주고 싶은 경우에는 label의 appearance관련 설정을 해야합니다.
let stringTypeLabel = {
let label = UILabel(frame: CGRect(origin: CGPoint(x: 20, y: 100), size: .zero))
let comment = "If not now, then when?"
label.text = comment
label.font = UIFont.systemFont(ofSize: CGFloat(20))
label.textColor = UIColor.red
label.backgroundColor = UIColor.yellow
label.sizeToFit()
return label
}()
Text에 변화를 주기 위해 label의 appearance 값을 지정했습니다. 이 경우 특정 substring만 text 색을 변경하거나, background color, bold 등을 변경할 수 없습니다. UI component 전체적으로 appearance가 변경됩니다. 부분적으로 특정 문자열 substring만 customizing할 순 없을까? 이때 사용되는 개념이 attributed string입니다. 이를 통해 substrings의 appearance를 커스텀할 수 있습니다. 위에서 설명했듯 attributed string은 NSAttributedString or NSMutableAttributedString의 init을 통해 만들 수 있습니다.
Attributed string은 UILabel appearance에 변화를 주는게 아닌 text 자체에 변화를 주는 object입니다. attributes가 적용된 각각의 substring range에 따라 text를 부분적으로 커스텀 할 수 있습니다.
What is difference between NSAttributedString and NSMutableAttributedString?
두 object의 차이는 결론적으로 read-only attributed string, modifiable attributed string. Mutable의 여부입니다. NSMutableAttributedString 또는 NSAttributedString은 init으로 attributedString을 생성할 수 있습니다. 생성 후 특정 범위의 attributedString에 attribute를 추가할 수 없는 것이 NSAttributedString입니다. NSMutableAttributedString은 mutable하게 값을 변경할 수 있습니다. 두 object다 CFAttributedString, CGMutableAttributedString으로 toll-free bridged할 수 있습니다.
위의 그림1. UILabel의 경우 부분적으로 커스터마이징한 text(attributedString)를 label.attributedText에 대입하면 됩니다. Attributed String은 range에 의해서 특정 subString의 attribute를 추가할 수 있습니다. 이때 range는 NSRange를 사용합니다. 범위에 따라서 다른 appearance를 적용할 수 있다는게 매력적입니다. attributedText와 text의 속도 비교는 gyuni님 블로그를 참고했습니다.
Attribute는 이름, value 또는 NSAttributedString.Key : value 타입의 NSDictionary object로 저장합니다. 이때 범위에 따라서 attrubute가 적용됩니다. 범위 지정은 정말 중요합니다!
Combine some Attributes with NSMutableAttribuedString
NSMutableAttributedString을 통해 세 범위에 따라 서로 다른 attribute를 추가해 아래와 같은 결과를 만들 것입니다.
let attributedStringTypeLabel = {
let label = UILabel(frame: CGRect(origin: CGPoint(x: 20, y: 150), size: .zero))
let comment = "If not now, then when?"
let attributedString = NSMutableAttributedString(string: comment) // 1
let prefixBoldAttribute: [NSAttributedString.Key : Any] = [.font : UIFont.boldSystemFont(ofSize: 40) ] // 2
let prefixSentenceBackgroundAttribute = [NSAttributedString.Key.backgroundColor : UIColor.yellow] // 3
let lastSentenceAttribute: [NSAttributedString.Key : Any] = [.foregroundColor : UIColor.systemPink , .font : UIFont.boldSystemFont(ofSize: 20)] // 4
attributedString.addAttributes(prefixBoldAttribute, range: NSRange(location: 0, length: 2)) // 5
attributedString.addAttributes(prefixSentenceBackgroundAttribute, range: NSRange(location: 0, length: 10)) // 6
attributedString.addAttributes(lastSentenceAttribute, range: (comment as NSString).range(of: "then when?")) // 7
label.attributedText = attributedString // 8
label.sizeToFit()
return label
}()
1. NSMutableAttributedString의 인자값 comment를 통해 attributedString을 만듭니다.
이제 attributedString property에 적용될 attribute들을 정의합니다.
2. prefixBoldAttribute의 타입은 [NSAttributedString.Key : Any] 타입의 NSDictionary object입니다. 앞에서 타입을 선언 했기에 .font 라는 NSAttributedString.Key 타입의 추론이 가능합니다. .font의 값인 value로써 UIFont.boldSystemFont(ofSize:)가 담겨있습니다.
3. prefixSentenceBackgroundAttribute property를 통해 콤마 앞 문장의 백그라운드 색의 attribute를 지정합니다. 2와 달리 타입을 표기하지 않았기에 NSAttributedString.Key.backgroundColor로 키 타입을 명시해야 합니다.
4. lastSentenceAttribute property를 통해 콤마 뒷 문장의 text color를 변경할 수 있는 attribute를 지정합니다.
attributedString에 대한 모든 속성 종류를 지정했다면 이제 addAttributes(_:range:) 함수를 통해 특정 범위에 속성을 추가하면 됩 니다.
5. attributedString.addAttributes( 속성 값, 범위!! ) 이때 범위는 NSRange를 통해 지정합니다. "If" characters관련 속성을 정의합니다.
location은 attributedString에서 attribute가 지정될 특정 index.
length는 길이입니다. ( 몇 개 character까지 지정할 것인가?)
6. 마찬가지로 특정 범위를 지정하고 속성을 부여합니다. "If not now" String의 background 컬러 지정
7. 마찬가지로 특정 범위를 지정하고 속성을 부여합니다. 이때 range는 (comment as NSString) 를 통해 NSString.range(of:) 를 사용할 수 있습니다.
8. 이렇게 속성과 범위에 따라서 속성을 추가한 attributedString을 UILabel의 attributedText에 대입하면 끝!
잘못된 내용이 있다면 알려주세요. 감사합니다.