rshev
rshev

Reputation: 4176

iOS 10.3: NSStrikethroughStyleAttributeName is not rendered if applied to a sub range of NSMutableAttributedString

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

Upvotes: 48

Views: 11411

Answers (7)

SerkanHocam
SerkanHocam

Reputation: 608

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

Upvotes: 0

Yuchen
Yuchen

Reputation: 33036

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

Upvotes: 10

tommybananas
tommybananas

Reputation: 5736

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

Upvotes: 93

AamirR
AamirR

Reputation: 12208

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)

Upvotes: 4

Velu Loganathan
Velu Loganathan

Reputation: 241

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

Upvotes: 10

Mugunth
Mugunth

Reputation: 14499

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.

Upvotes: 26

rshev
rshev

Reputation: 4176

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.

Upvotes: 14

Related Questions