12/08/2018, 17:00

Attributed String in Swift: The right way

Attributed String in Swift: The right way Tôi thường gặp phải khá nhiều trường hợp phải sử dụng Attributed String vì những yêu cầu quái dở của khách hàng =)) hoặc đội Design hơi phóng túng và phong lưu. Hồi mới code objective-C thì mỗi lần động đến Attributed String tôi luôn thấy rất khó chịu và ...

Attributed String in Swift: The right way

Tôi thường gặp phải khá nhiều trường hợp phải sử dụng Attributed String vì những yêu cầu quái dở của khách hàng =)) hoặc đội Design hơi phóng túng và phong lưu. Hồi mới code objective-C thì mỗi lần động đến Attributed String tôi luôn thấy rất khó chịu và mệt mỏi vì code rất mất thời gian với việc create từng đoạn string một, combine lại rồi apply style cho nó,... Bây giờ thì code swift và tôi tìm đc 1 bài viết rất hay về vấn đề này. Tác giả Daniele Margutti đã viết 1 library SwiftRichString để dành riêng cho việc xử lý attributed string trong swift: github mà chúng ta sẽ cùng xem xét như trong bài giới thiệu của tác giả ở link cuối bài.

Type Safety:

Swift implements type safety: Có nghĩa là bạn không thể sử dụng 1 variable vào những việc mà kiểu của nó không cho phép. Nói cách khác, theo quy tắc về "type safety", bạn có thể chia cho 0, đó không phải là type error nhưng bạn không thể thực hiện phép toán số học với 1 string trừ khi bạn convert nó trước.

Type safety có thể được mô tả trong một đặc điểm của ngôn ngữ và được thi hành trong 1 một trường phát triển như Xcode; nó cũng có thể được thi hành với việc lập trình cẩn trọng. Nếu ko có Type safety rất dễ để tạo thành những code không an toàn.

NSAttributedStrings sử dụng NSDictionary để set các thuộc tính cho text, các id của thuộc tính (kiểu strings) được truyền vào cùng với value của nó như sau:

shadow.shadowOffset = CGSizeMake(-2.0, -2.0)
let attributes = [
        NSFontAttributeName : titleFont,
        NSUnderlineStyleAttributeName : 1,
        NSForegroundColorAttributeName : UIColor.whiteColor(),
        NSTextEffectAttributeName : NSTextEffectLetterpressStyle, 
        NSStrokeWidthAttributeName : 3.0,
        NSShadowAttributeName : shadow
]
 
var title = NSAttributedString(string: "NSAttributedString", attributes: attributes)

Và để làm cho đoạn code trên an toàn hơn, tác giả đã tạo ra 1 khái niệm là Style: Bạn sẽ tạo ra 1 Style với tên của nó và gán các thuộc tính vào bằng cách sử dụng creational pattern truyền thống:

let myStyle = Style("super", {
  $0.font = FontAttribute(.TimesNewRomanPS_BoldItalicMT, size: 40) // font + size
  $0.underline = UnderlineAttribute(color: UIColor.blue, style: NSUnderlineStyle.styleSingle) // underline attributes
  $0.color = UIColor(hex: "#FF4555") // text color
  $0.align = .center // text alignment
})

Ngay cả thuộc tính của font cũng là type safe, bạn có thể tạo 1 đối tượng UIFont theo kiểu type-safe bằng cách chỉ định 1 trong nhũng font mà iOS cung cấp sẵn có. FontAttribute cũng có thể được mở rộng và thêm tên font chữ của riêng bạn. Khi nhìn vào thư viện SwiftRichString bạn có thể thấy 1 số cấu trúc khác như StrikeAttribute, ShadowAttribute hoặc UnderlineAttribute... tất cả đều được tạo ra để làm mọi thứ rõ ràng và nhanh chóng hơn trong việc sử dụng. Điều lý tưởng của Style đó là bạn có thể tạo ra được các bộ styles mà bạn có thể sử dụng trong code của các app tiếp theo. Có 1 kiểu Style đặc biệt duy nhất là .default: Nó được apply mặc định ở vị trí đầu tiên ko phụ thuộc vào thứ tự mà nó đc truyền cho các tham số trong 1 functions của styles. Điểu này đảm bảo bạn có thể có 1 base common style cho string của bạn và thêm vào 1 hoặc nhiều thuộc tính cho string đó 1 cách dễ dàng. Nếu bạn không cần sử dụng tagged string trong code, bạn có thể tạo 1 object Stylemà không có tên.

Painless Attributed String Creation and Management

Create 1 NSAttributedString, combine lại rồi apply style cho nó là cực kỳ tẻ nhạt và rườm rà và rất nhiều code dài dòng:

let yourAttributes = [
  NSForegroundColorAttributeName: UIColor.blackColor(),
  NSFontAttributeName: UIFont.systemFontOfSize(15)]

let yourOtherAttributes = [
  NSForegroundColorAttributeName: UIColor.redColor(),
  NSFontAttributeName: UIFont.systemFontOfSize(25)]

let partOne = NSMutableAttributedString(string: "This is an example ", attributes: yourAttributes)
let partTwo = NSMutableAttributedString(string: "for the combination of Attributed String!", attributes: yourOtherAttributes)

let combination = NSMutableAttributedString()

combination.appendAttributedString(partOne)
combination.appendAttributedString(partTwo)

Chúng ta có thể làm tốt hơn như sau:

et titleStyle,highglited = // just define your own style
// Let's combine simple String, String with applied attributes, as like you can imagine
let attributedText = "Hello" + username.set(style: titleStyle) + "," + " welcome here".set(style: highglited)

function set() cho phép bạn add thuộc tính vào 1 String đã có hoặc append thuộc tính trực tiếp cho 1 instance của NSAttributedString . Có vài kiểu set() mà cho phép bạn thiết lập các thuộc tính cho toàn bộ string hoặc 1 phần và cũng có thể hỗ trợ cho việc kết hợp pattern matching thông qua regular expression. Ví dụ như sau:

// Apply Style to String
var test_1 = "Hello".set(style: bold) + "
" + "World!".set(style: highlighted)

// Apply Multiple Styles to a String (.default is set in first place)
var test_2 = "Hello".set(styles: mixStyle1,mixStyle2,defaultStyle)

// Apply Style with Pattern Matching
let test_3 = "prefix12 aaa3 prefix45".set(styles: bold, pattern: "fix([0-9])([0-9])", options: .caseInsensitive)
let test_4 = "            
0