48

A strikethrough (single, double, ...) added as attribute to an instance of NSMutableAttributedString is not rendered if the apply range is not the whole string range.

This happens using addAttribute(_ name: String, value: Any, range: NSRange), insert(_ attrString: NSAttributedString, at loc: Int), append(_ attrString: NSAttributedString), ...

Broken by Apple in early iOS 10.3 betas, and not fixed in 10.3 final.

Credit: https://openradar.appspot.com/31034683

rshev
  • 4,086
  • 1
  • 23
  • 32

7 Answers7

93

Setting the baseline offset seems to fix it:

[attributedStr addAttribute:NSBaselineOffsetAttributeName value:@0 range:NSMakeRange(0, 10)];
[attributedStr addAttribute:NSStrikethroughStyleAttributeName value:@2 range:NSMakeRange(0, 10)];

This is a known bug in iOS 10.3

tommybananas
  • 5,718
  • 1
  • 28
  • 48
26

Adding a NSBaselineOffsetAttributeName, as explained here, to the attributed string brings back the strikethrough line. Overriding drawText:in: can be slow especially on Collection View or Table View Cells.

Mugunth
  • 14,461
  • 15
  • 66
  • 94
14

Found a workaround for our specific scenario (we don't specify any styling with UILabel's properties, but all with NSAttributedString attributes):

/// This UILabel subclass accomodates conditional fix for NSAttributedString rendering broken by Apple in iOS 10.3
final class PriceLabel: UILabel {

    override func drawText(in rect: CGRect) {
        guard let attributedText = attributedText else {
            super.drawText(in: rect)
            return
        }

        if #available(iOS 10.3, *) {
            attributedText.draw(in: rect)
        } else {
            super.drawText(in: rect)
        }
    }
}

NOTE: if you mix UILabel's styling properties with NSAttributedString attributes, you should think of creating a new attributed string before rendering, apply UILabel's styling on it and then re-apply all attributedText's attributes over it.

rshev
  • 4,086
  • 1
  • 23
  • 32
  • 1
    This as been killing me all day, so glad you found a solution – rmigneco Mar 28 '17 at 18:16
  • For what it's worth, I've also found that for iOS 10.3 release, attributed strings are not rendered with the font style set on the UILabel property. So for example, it is not enough to specify the UILabel's font type if you are using attributed Text property. You must include the desired font via NSFontAttributeName as an attribute – rmigneco Mar 29 '17 at 19:00
  • when i override drawText function as you said, it will ignore all label style i gave in storyboard (alignment, font, color). is there anyway to cast all these styles to NSMutableAttributedString's attribute? – Mohammadalijf Mar 30 '17 at 19:55
  • @Mohammadalijf that what I put into the note underneath my answer - read them from the label's properties and reapply as `NSAttributedString` attributes. – rshev Mar 30 '17 at 19:59
10

swift 3 working code tested with 10.3

let attributeString: NSMutableAttributedString = NSMutableAttributedString(string: "₹3500")
attributeString.addAttribute(NSBaselineOffsetAttributeName, value: 0, range: NSMakeRange(0, attributeString.length))
attributeString.addAttribute(NSStrikethroughStyleAttributeName, value: 1, range: NSMakeRange(0, attributeString.length))
productPriceLabel.attributedText = attributeString
Velu Loganathan
  • 241
  • 2
  • 11
10

Swift 4

let text = "Hello World"
let textRange = NSMakeRange(0, text.count)
let attributedText = NSMutableAttributedString(string: text)
attributedText.addAttribute(NSAttributedStringKey.strikethroughStyle,
                            value: NSUnderlineStyle.styleSingle.rawValue,
                            range: textRange)
myLabel.attributedText = attributedText
Yuchen
  • 30,852
  • 26
  • 164
  • 234
  • 1
    Great! Had to spent almost an hour to find out those keys `NSAttributedStringKey.strikethroughStyle`, etc. – Pei Apr 20 '18 at 19:30
  • 7
    The point is use ```NSUnderlineStyle.styleSingle.rawValue``` not ```NSUnderlineStyle.styleSingle```. – ZYiOS Jun 18 '18 at 12:42
4

Its a bug known to Xcode 8.3 (8E3004b) / iOS 10.3, with this workaround NO extra line of code is need, just add [NSBaselineOffsetAttributeName:0] when declaring NSMutableAttributedString()

let attrStr = NSMutableAttributedString(string: YOUR_STRING_HERE, attributes: [NSBaselineOffsetAttributeName : 0])

// Now if you add the strike-through attribute to a range, it will work
attrStr.addAttributes([
    NSFontAttributeName: UIFont.boldSystemFont(ofSize: 24),
    NSStrikethroughStyleAttributeName: 1
], range: NSRange)
AamirR
  • 11,672
  • 4
  • 59
  • 73
0

For Swift 5 NSBaselineOffsetAttributeName changed with kCTBaselineOffsetAttributeName and will change every new version.

SerkanHocam
  • 536
  • 6
  • 18