15

I have a string which has no whitespace at end of the string but when I am converting to NSAttributedString and set to UITextView it was seen some whitespace at end of the UILabel.

For making NSAttributedString I am using following code. In my code expectedLabelSize gives a big height.

UILabel *tempLbl = [[UILabel alloc]init];
tempLbl.font = txtView.font;
tempLbl.text = string;

NSDictionary *dictAttributes = [NSDictionary dictionaryWithObjectsAndKeys: tempLbl.font, NSFontAttributeName, aParaStyle, NSParagraphStyleAttributeName,[UIColor darkGrayColor],NSForegroundColorAttributeName, nil];

CGSize expectedLabelSize = [string boundingRectWithSize:maximumLabelSize options:NSStringDrawingUsesLineFragmentOrigin attributes:dictAttributes context: nil].size;
Vvk
  • 4,031
  • 29
  • 51
  • Update your question with the code that initializes `string`. Why does your question talk about a `UITextView`? Your posted code makes no mention of a text view. And what does the `UILabel` have to do with the question? It has no bearing on `expectedLabelSize`. – rmaddy Dec 04 '15 at 05:09
  • Did you try setting to 0 and then do sizeToFit ? – Abhishek Bedi Dec 04 '15 at 05:14
  • **Check This** [Trim White Space and newLine characters from front and end of NSAttributedString](https://stackoverflow.com/a/54900313/4469784) – kamalraj venkatesan Feb 27 '19 at 08:25

4 Answers4

24

Swift answer but it give you a start if you translate it to Obj-C (or make a swift file just with the extension for use in your Obj-C then)

extension NSMutableAttributedString {

    func trimmedAttributedString(set: CharacterSet) -> NSMutableAttributedString {

        let invertedSet = set.inverted

        var range = (string as NSString).rangeOfCharacter(from: invertedSet)
        let loc = range.length > 0 ? range.location : 0

        range = (string as NSString).rangeOfCharacter(
                            from: invertedSet, options: .backwards)
        let len = (range.length > 0 ? NSMaxRange(range) : string.characters.count) - loc

        let r = self.attributedSubstring(from: NSMakeRange(loc, len))
        return NSMutableAttributedString(attributedString: r)
    }
}

Usage :

let noSpaceAttributedString =
   attributedString.trimmedAttributedString(set: CharacterSet.whitespacesAndNewlines)
Fattie
  • 27,874
  • 70
  • 431
  • 719
Dean
  • 1,512
  • 13
  • 28
  • 1
    Thanks Dean, superb. I edited in the minor changes for the latest Swift. Awesome thanks – Fattie May 16 '17 at 23:19
  • 1
    This does work very good, except if the white space is in the middle of the text. Any idea on how to fix this ? – r3dm4n Jul 14 '18 at 07:57
10

Swift 4 and above

we can create extension on NSMutableAttributedString which returns new NSAttributedString by removing .whitespacesAndNewlines

extension NSMutableAttributedString {

    func trimmedAttributedString() -> NSAttributedString {
        let invertedSet = CharacterSet.whitespacesAndNewlines.inverted
        let startRange = string.rangeOfCharacter(from: invertedSet)
        let endRange = string.rangeOfCharacter(from: invertedSet, options: .backwards)
        guard let startLocation = startRange?.upperBound, let endLocation = endRange?.lowerBound else {
            return NSAttributedString(string: string)
        }
        let location = string.distance(from: string.startIndex, to: startLocation) - 1
        let length = string.distance(from: startLocation, to: endLocation) + 2
        let range = NSRange(location: location, length: length)
        return attributedSubstring(from: range)
    }
}
Suhit Patil
  • 11,748
  • 3
  • 50
  • 60
  • 2
    This will crash if the last character in the string is an emoji! – Alex Curylo Jan 10 '20 at 12:44
  • I translated this solution to ObjectiveC (with some modifications for my need --- extended NSMutableAttributedString with 'trimWhiteSpace' on self) but I need some explanation on the calculations that hop between the Swift string (startIndex, upperBound etc) and the original NSString and NSRange numbers. could you elaborate just a little? – Motti Shneor Aug 16 '21 at 10:46
  • There isn't much sense in extending NSMutableAttributedString without actually allowing to change it. This extension is more suitable for the immutable NSAttributedString - to return a trimmed version of itself. An extension to NSMutableAttributedString should affect the received itself - see my Obj-C extension – Motti Shneor Aug 16 '21 at 11:00
2
extension NSAttributedString {
    func trimmedAttributedString() -> NSAttributedString {
        let invertedSet = CharacterSet.whitespacesAndNewlines.inverted
        let startRange = string.rangeOfCharacter(from: invertedSet)
        let endRange = string.rangeOfCharacter(from: invertedSet, options: .backwards)
        guard let startLocation = startRange?.lowerBound, let endLocation = endRange?.lowerBound else {
            return self
        }
        let range = NSRange(startLocation...endLocation, in: string)
        return attributedSubstring(from: range)
    }
}
1

Here's an Obj-C rendering of @suhit Patil's Swift NSAttributedString extension - but this time, it's on NSMutableAttributedString and acts on self. Hope it'll be of use to someone.

@interface NSMutableAttributedString (OITTrimming)

- (void)trimWhiteSpace;

@end

@implementation NSMutableAttributedString (OITTrimming)

 /*!
  @abstract trims whitespacesAndNewlines off the receiver
*/
- (void)trimWhiteSpace {
    NSCharacterSet *legalChars = [[NSCharacterSet whitespaceAndNewlineCharacterSet] invertedSet];
    NSRange startRange = [self.string rangeOfCharacterFromSet: legalChars];
    NSRange endRange = [self.string rangeOfCharacterFromSet: legalChars options:NSBackwardsSearch];
    if (startRange.location == NSNotFound || endRange.location == NSNotFound) {
        // there are no legal characters in self --- it is ALL whitespace, and so we 'trim' everything leaving an empty string
        [self setAttributedString:[NSAttributedString new]];
    }
    else {
        NSUInteger startLocation = NSMaxRange(startRange), endLocation = endRange.location;
        NSRange range = NSMakeRange(startLocation - 1, endLocation - startLocation + 2);
        [self setAttributedString:[self attributedSubstringFromRange:range]];
    }
}

@end
Motti Shneor
  • 2,095
  • 1
  • 18
  • 24