Adjust items after string manipulation in AttributedString - Swift 4

I have the following that allows me to create a list of markers that works very well, however, after creating the list of markers, I need to manipulate the output Attributed line to have separate elements in bold or italics, or both.

I have a function:

@IBOutlet var label: UILabel!
let bulletString = ["String 1","String 2","String 3"]

label.attributedText = label.bulletPoints(stringList: bulletString, font: UIFont.stdFontMediumSeventeen, bullet: "•", lineSpacing: 4, paragraphSpacing: 4, textColor: UIColor.darkGreyColor, bulletColor: UIColor.darkGreyColor)

func bulletPoints(stringList: [String],font: UIFont,bullet: String = "\u{2022}",indentation: CGFloat = 20,lineSpacing: CGFloat = 2,paragraphSpacing: CGFloat = 12,textColor: UIColor = .gray,bulletColor: UIColor = .red) -> NSAttributedString{
    let textAttributes: [NSAttributedStringKey: Any] = [NSAttributedStringKey.font: font, NSAttributedStringKey.foregroundColor: textColor]
    let bulletAttributes: [NSAttributedStringKey: Any] = [NSAttributedStringKey.font: font, NSAttributedStringKey.foregroundColor: bulletColor]

    let paragraphStyle = NSMutableParagraphStyle()
    let nonOptions = [NSTextTab.OptionKey: Any]()
    paragraphStyle.tabStops = [NSTextTab(textAlignment: .left, location: indentation, options: nonOptions)]
    paragraphStyle.defaultTabInterval = indentation
    paragraphStyle.lineSpacing = lineSpacing
    paragraphStyle.paragraphSpacing = paragraphSpacing
    paragraphStyle.headIndent = indentation

    let bulletList = NSMutableAttributedString()
    for string in stringList {
        let formattedString = "\(bullet)\t\(string)\n"
        let attributedString = NSMutableAttributedString(string: formattedString)

        attributedString.addAttributes(
            [NSAttributedStringKey.paragraphStyle : paragraphStyle],
            range: NSMakeRange(0, attributedString.length))

        attributedString.addAttributes(
            textAttributes,
            range: NSMakeRange(0, attributedString.length))

        let string:NSString = NSString(string: formattedString)
        let rangeForBullet:NSRange = string.range(of: bullet)
        attributedString.addAttributes(bulletAttributes, range: rangeForBullet)
        bulletList.append(attributedString)
    }
    return bulletList
}

What I'm looking for is a way to convey a boolean state if the bullet string requires bold or italic text, and if so, which elements of the intital string need this treatment.

The function bulletPointsis in the extension file and works as expected.

+6
source share
3 answers

/ , , . , , .

bulletPoints, . ( , UILabel?), , . ,

class ViewController: UIViewController {

    @IBOutlet var label: UILabel!


    override func viewWillAppear(_ animated: Bool) {
        let bulletStrings = [BulletString(string: "String 1", traits: []),
                             BulletString(string: "String 2", traits: [.traitBold]),
                             BulletString(string: "String 3", traits: [.traitItalic]),
                             BulletString(string: "String 4", traits: [.traitBold, .traitItalic])]
        label.attributedText = bulletPoints(stringList: bulletStrings, font: UIFont.systemFont(ofSize: 15.0), bullet: "•", lineSpacing: 4, paragraphSpacing: 4, textColor: UIColor.darkGray, bulletColor: UIColor.darkGray)

    }

    func bulletPoints(stringList: [BulletString],
                      font: UIFont,
                      bullet: String = "\u{2022}",
                      indentation: CGFloat = 20,
                      lineSpacing: CGFloat = 2,
                      paragraphSpacing: CGFloat = 12,
                      textColor: UIColor = .gray,
                      bulletColor: UIColor = .red) -> NSAttributedString {

        let bulletList = NSMutableAttributedString()
        for bulletString in stringList {
            let attributedString = NSMutableAttributedString(string: "")
            let bulletAttributes: [NSAttributedStringKey: Any] = [
                .foregroundColor: bulletColor,
                .font: font]
            attributedString.append(NSAttributedString(string: bullet, attributes: bulletAttributes))

            let textAttributes: [NSAttributedStringKey: Any] = [
                .font: font.withTraits(traits: bulletString.traits),
                .foregroundColor: textColor,
                .paragraphStyle : paragraphStyle(indentation: indentation, lineSpacing: lineSpacing, paragraphSpacing: paragraphSpacing)
            ]
            attributedString.append(NSAttributedString(string:"\t\(bulletString.string)\n", attributes: textAttributes))

            bulletList.append(attributedString)
        }
        return bulletList
    }

    private func paragraphStyle(indentation: CGFloat, lineSpacing: CGFloat, paragraphSpacing: CGFloat) -> NSParagraphStyle {
        let style = NSMutableParagraphStyle()
        let nonOptions = [NSTextTab.OptionKey: Any]()
        style.tabStops = [NSTextTab(textAlignment: .left, location: indentation, options: nonOptions)]
        style.defaultTabInterval = indentation
        style.lineSpacing = lineSpacing
        style.paragraphSpacing = paragraphSpacing
        style.headIndent = indentation
        return style
    }
}

struct BulletString {
    let string: String
    let traits: UIFontDescriptorSymbolicTraits
}

extension UIFont {

    func withTraits(traits:UIFontDescriptorSymbolicTraits...) -> UIFont {
        let descriptor = self.fontDescriptor
            .withSymbolicTraits(UIFontDescriptorSymbolicTraits(traits))!
        return UIFont(descriptor: descriptor, size: 0)
    }

}

Example Results

, , ,

+4

. , , . .

let fullString = "Bold normal italic"
let attrString = NSMutableAttributedString(string: fullString, attributes: [.font: UIFont.systemFont(ofSize: 18.0)])

let range1 = (fullString as NSString).range(of: "Bold")
let range2 = (fullString as NSString).range(of: "italic")
attrString.addAttributes([.font: UIFont.boldSystemFont(ofSize: 20.0)], range: range1)
attrString.addAttributes([.font: UIFont.boldSystemFont(ofSize: 20.0).italics()], range: range2)

label.attributedText = attrString

UIFont.

extension UIFont {
    func withTraits(_ traits: UIFontDescriptorSymbolicTraits) -> UIFont {
        if let fd = fontDescriptor.withSymbolicTraits(traits) {
            return UIFont(descriptor: fd, size: pointSize)
        }
        return self
    }

    func italics() -> UIFont {
        return withTraits(.traitItalic)
    }
}

, , , , . , NSString.range(of:) .

. , start endIndex. SO. , . .

+1

, , - stringList.

BulletString:

class BulletString {
   var text: String
   var attributes: [NSAttributedStringKey : Any]?

   init(string: String) {
      text = string
   } 
}

Now your bullet stringListin your function should be [BulletString]. Define two string bulletStrings and pass them to your function. Here is a working solution with your function:

    let bulletString1 = BulletString.init(string: "string1")
    bulletString1.attributes = [NSAttributedStringKey.font: UIFont.boldSystemFont(ofSize: 18.0)]

    let bulletString2 = BulletString.init(string: "string2")

    let bullets = [bulletString1, bulletString2]

    label.attributedText = bulletPoints(stringList: bullets, font: UIFont.systemFont(ofSize: 17), bullet: "•", lineSpacing: 4, paragraphSpacing: 4, textColor: UIColor.darkGray, bulletColor: UIColor.darkGray)
    label.textColor = .black

func bulletPoints(stringList: [BulletString], font: UIFont,bullet: String = "\u{2022}",indentation: CGFloat = 20,lineSpacing: CGFloat = 2,paragraphSpacing: CGFloat = 12,textColor: UIColor = .gray,bulletColor: UIColor = .red) -> NSAttributedString{
    let textAttributes: [NSAttributedStringKey: Any] = [NSAttributedStringKey.font: font, NSAttributedStringKey.foregroundColor: textColor]
    let bulletAttributes: [NSAttributedStringKey: Any] = [NSAttributedStringKey.font: font, NSAttributedStringKey.foregroundColor: bulletColor]

    let paragraphStyle = NSMutableParagraphStyle()
    let nonOptions = [NSTextTab.OptionKey: Any]()
    paragraphStyle.tabStops = [NSTextTab(textAlignment: .left, location: indentation, options: nonOptions)]
    paragraphStyle.defaultTabInterval = indentation
    paragraphStyle.lineSpacing = lineSpacing
    paragraphStyle.paragraphSpacing = paragraphSpacing
    paragraphStyle.headIndent = indentation

    let bulletList = NSMutableAttributedString()
    for bulletString in stringList {
      let formattedString = "\(bullet)\t\(bulletString.text)\n"
      let attributedString = NSMutableAttributedString(string: formattedString)

      attributedString.addAttributes(
        [NSAttributedStringKey.paragraphStyle : paragraphStyle],
        range: NSMakeRange(0, attributedString.length))

      attributedString.addAttributes(
        textAttributes,
        range: NSMakeRange(0, attributedString.length))

      // Here your custom attributes you provided in BulletString
      if let attr = bulletString.attributes {
        attributedString.addAttributes(attr, range: NSMakeRange(0, attributedString.length))
      }


      let string:NSString = NSString(string: formattedString)
      let rangeForBullet:NSRange = string.range(of: bullet)
      attributedString.addAttributes(bulletAttributes, range: rangeForBullet)

      bulletList.append(attributedString)
    }
    return bulletList
}

results

enter image description here

0
source

All Articles