12

I'm moving from swift 3 to swift 4. I have UILabels that I am giving very specific text properties to the label. I'm getting an 'unexpectedly found nil while unwrapping optional value' error when strokeTextAttributes is being initialized. I'm totally lost to be frank.

In swift 3 the of strokeTextAttributes was [String : Any] but swift 4 threw errors until I changed it to what it is below.

let strokeTextAttributes = [
    NSAttributedStringKey.strokeColor.rawValue : UIColor.black,
    NSAttributedStringKey.foregroundColor : UIColor.white,
    NSAttributedStringKey.strokeWidth : -2.0,
    NSAttributedStringKey.font : UIFont.boldSystemFont(ofSize: 18)
    ] as! [NSAttributedStringKey : Any]


chevronRightLabel.attributedText = NSMutableAttributedString(string: "0", attributes: strokeTextAttributes)
Krunal
  • 77,632
  • 48
  • 245
  • 261
Blue
  • 1,408
  • 4
  • 17
  • 36
  • 4
    `NSAttributedStringKey.strokeColor.rawValue` => `NSAttributedStringKey.strokeColor` instead? – Larme Oct 09 '17 at 13:18

7 Answers7

31

@Larme's comment about the .rawValue not being needed is correct.

Also, you can avoid the force cast that crashes your code using explicit typing:

let strokeTextAttributes: [NSAttributedString.Key: Any] = [
    .strokeColor : UIColor.black,
    .foregroundColor : UIColor.white,
    .strokeWidth : -2.0,
    .font : UIFont.boldSystemFont(ofSize: 18)
]

This gets rid of the repetitive NSAttributedString.Key., too.

Xavier Lowmiller
  • 1,381
  • 1
  • 15
  • 24
  • and if I want use the dic and specify range at same time, is there a way to do that? – Neko Jan 25 '18 at 09:53
  • All the `static let`s from [NSAttributedStringKey](https://developer.apple.com/documentation/foundation/nsattributedstringkey) can be used, so range isn't supported as far as I can tell. – Xavier Lowmiller Jan 25 '18 at 10:29
16

In Swift 4.0+, attributed string accepts json (dictionary) with key type NSAttributedStringKey or NSAttributedString.Key.

So you must change it from [String : Any] to

Swift 4.1 & below - [NSAttributedStringKey : Any] &
Swift 4.2 & above - [NSAttributedString.Key : Any]

Swift 4.2

Initialiser for AttributedString in Swift 4.2 is changed to [NSAttributedString.Key : Any]?

public init(string str: String, attributes attrs: [NSAttributedString.Key : Any]? = nil)

Here is sample working code.

let label = UILabel()
let labelText = "String Text"

let strokeTextAttributes = [
     NSAttributedString.Key.strokeColor : UIColor.black,
     NSAttributedString.Key.foregroundColor : UIColor.white,
     NSAttributedString.Key.strokeWidth : -2.0,
     NSAttributedString.Key.font : UIFont.boldSystemFont(ofSize: 18)
   ] as [NSAttributedString.Key : Any]

label.attributedText = NSAttributedString(string: labelText, attributes: strokeTextAttributes)

Swift 4.0

Initialiser for AttributedString in Swift 4.0 is changed to [NSAttributedStringKey : Any]?.

public init(string str: String, attributes attrs: [NSAttributedStringKey : Any]? = nil)

Here is sample working code.

let label = UILabel()
let labelText = "String Text"

let strokeTextAttributes = [
     NSAttributedStringKey.strokeColor : UIColor.black,
     NSAttributedStringKey.foregroundColor : UIColor.white,
     NSAttributedStringKey.strokeWidth : -2.0,
     NSAttributedStringKey.font : UIFont.boldSystemFont(ofSize: 18)
   ] as [NSAttributedStringKey : Any]

label.attributedText = NSAttributedString(string: labelText, attributes: strokeTextAttributes)

Look at this Apple Document, for more info: NSAttributedString - Creating an NSAttributedString Object

Krunal
  • 77,632
  • 48
  • 245
  • 261
1

NSAttributedStringKey.strokeColor.rawValue is of type String

NSAttributedStringKey.strokeColor is of type NSAttributedStringKey

So its unable to convert String to NSAttributedStringKey . You have to use like below:

let strokeTextAttributes: [NSAttributedStringKey : Any] = [
    NSAttributedStringKey.strokeColor : UIColor.black,
    NSAttributedStringKey.foregroundColor : UIColor.white,
    NSAttributedStringKey.strokeWidth : -2.0,
    NSAttributedStringKey.font : UIFont.boldSystemFont(ofSize: 18)
]
Community
  • 1
  • 1
Vini App
  • 7,339
  • 2
  • 26
  • 43
0

Swift 4 Attributed text with multiple colors

extension NSMutableAttributedString 
{
@discardableResult func DustyOrange(_ text: String, Fontsize : CGFloat) -> NSMutableAttributedString 
{
    let attrs: [NSAttributedStringKey: Any] = [.font: UIFont(name: "SFUIDisplay-Regular", size: Fontsize)!, NSAttributedStringKey.foregroundColor: UIColor(red: 242.0/255.0, green: 97.0/255.0, blue: 0.0/255.0, alpha: 1.0) ]
    let boldString = NSMutableAttributedString(string:text, attributes: attrs)
    append(boldString)
    return self
}
@discardableResult func WarmGrey(_ text: String, Fontsize : CGFloat) -> NSMutableAttributedString {
    let attrs: [NSAttributedStringKey: Any] = [.font: UIFont(name: "SFUIDisplay-Regular", size: Fontsize)!, NSAttributedStringKey.foregroundColor: UIColor(red: 152.0/255.0, green: 152.0/255.0, blue: 152.0/255.0, alpha: 1.0) ]
    let boldString = NSMutableAttributedString(string:text, attributes: attrs)
    append(boldString)
    return self
}
}

Now you can Execute the function something like this to use as a globally

func FormattedString(Orange : String, WarmGrey : String ,fontsize : CGFloat) -> NSMutableAttributedString 
{
   let paragraphStyle = NSMutableParagraphStyle()
   paragraphStyle.alignment = .left
   paragraphStyle.lineSpacing = 1
   paragraphStyle.paragraphSpacing = 1
   let formattedString = NSMutableAttributedString()
   formattedString
    .DustyOrange(Orange, Fontsize: fontsize)
    .WarmGrey(WarmGrey, Fontsize: fontsize )
  formattedString.addAttributes([NSAttributedStringKey.paragraphStyle: paragraphStyle], range: NSRange(location: 0, length: formattedString.length))
   return formattedString
}

You can use globalized function like this

 yourLabelName.attributedText = FormattedString(Orange: "String with orange color", WarmGrey: " String with warm grey color.", fontsize: 11.5)

Attributed text with image

func AttributedTextwithImgaeSuffix(AttributeImage : UIImage , AttributedText : String , buttonBound : UIButton) -> NSMutableAttributedString 
 {
   let fullString = NSMutableAttributedString(string: AttributedText + "  ")
   let image1Attachment = NSTextAttachment()
   image1Attachment.bounds = CGRect(x: 0, y: ((buttonBound.titleLabel?.font.capHeight)! - 
  AttributeImage.size.height).rounded() / 2, width: 
  AttributeImage.size.width, height: AttributeImage.size.height)
  image1Attachment.image = AttributeImage
  let image1String = NSAttributedString(attachment: image1Attachment)
  fullString.append(image1String)
  fullString.append(NSAttributedString(string: ""))
  return fullString 
}

you can use "NSTextAttachment" with your button label like this.

   yourUIButton.setAttributedTitle(AttributedTextwithImgaeSuffix(AttributeImage: desiredImage, AttributedText: "desired UIButton title", buttonBound: yourUIButton), for: .normal)
Azharhussain Shaikh
  • 1,654
  • 14
  • 17
0

In Swift 4.x, this should look like :

        let strokeTextAttributes: [NSAttributedStringKey: Any] = [
        NSStrokeColorAttributeName: UIColor.black,
        NSForegroundColorAttributeName : UIColor.white,
        NSStrokeWidthAttributeName : -2.0,
        NSFontAttributeName : UIFont.boldSystemFont(ofSize: 18)
    ]
Arunabh Das
  • 13,212
  • 21
  • 86
  • 109
  • `NSForegroundColorAttributeName` That's an old name, no? That's not a `NSAttributedStringKey`. – Larme Jun 18 '18 at 18:49
  • It is an NSAttributedStringKey according to https://developer.apple.com/documentation/uikit/nsforegroundcolorattributename – Arunabh Das Jun 18 '18 at 22:35
  • In Objective-C, not in Swift. If I put that code in Swift, it says "Use of unresolved identifier" for each one. – Larme Jun 18 '18 at 22:44
0

if you want to change particular string value so that below answer is helpful you:-

let subStr = "Hello" let allStr = "Hello World"

    let newStr = NSMutableAttributedString(string: allStr)
    newStr.addAttribute(kCTFontAttributeName as NSAttributedStringKey, value:  UIFont.init(customFont: .MyriadPro_R, withSize: 18)!, range: (allStr as NSString).range(of: subStr))
    newStr.addAttribute(NSAttributedStringKey.foregroundColor, value: UIColor.PrimaryColor, range: (allStr as NSString).range(of: subStr))
    self.stateLbl.attributedText = newStr
0
let text = systolicString + " / " + diastolicString

let newStr = NSMutableAttributedString(string: text)
// I have static ranges, but you can also extract them dynamically
let systolicRange = NSRange(location: 0, length: 2)
let backslashRange = NSRange(location: 3, length: 1)
let diastolicRange = NSRange(location: 5, length: 2)

newStr.addAttribute(NSAttributedStringKey.font, value:  UIFont.ubuntuRegular(28), range: systolicRange)
newStr.addAttribute(NSAttributedStringKey.foregroundColor, value: UIColor(hexString: "042f57"), range: systolicRange)

newStr.addAttribute(NSAttributedStringKey.font, value:  UIFont.ubuntuLight(23), range: backslashRange)
newStr.addAttribute(NSAttributedStringKey.foregroundColor, value: UIColor(hexString: "6485a3"), range: backslashRange)

newStr.addAttribute(NSAttributedStringKey.font, value:  UIFont.ubuntuRegular(18), range: diastolicRange)
newStr.addAttribute(NSAttributedStringKey.foregroundColor, value: UIColor(hexString: "042f57"), range: diastolicRange)
// my UILabel
valueLabel.attributedText = newStr
Dani Pralea
  • 4,545
  • 2
  • 31
  • 49