How to change truncate characters in UILabel?

When the text of a UILabel gets truncated there are 3 dots inserted by default. Is it possible to change these characters or disable them?


Solution 1:

I have written a custom truncating class that you can pop into you code where ever. Just use this method below. it will return true if truncation has taken place, and MaxWidth can be left as 0 if you just want to use the labels default frame width. Put maxWidth as something less than the frames width to shorten it within its frames bounds.

Swift 2 (with some swift 3 comments for converting)

usage:

Truncater.replaceElipsis(forLabel: label, withString: "???")
let didTruncate = Truncater.replaceElipsis(forLabel: label, withString: "1234", andMaximumWidth: 50) //maxWidth is not number of chars, but label width in CGFloat

class:

import UIKit

class Truncater {

    class func replaceElipsis(forLabel label:UILabel, withString replacement:String) -> Bool {
        return replaceElipsis(forLabel: label, withString: replacement, andMaximumWidth:0)
    }

    class func replaceElipsis(forLabel label:UILabel, withString replacement:String, andMaximumWidth width:CGFloat) -> Bool {

        if(label.text == nil){
            return false
        }

        let origSize = label.frame;
        var useWidth = width

        if(width <= 0){
            useWidth = origSize.width //use label width by default if width <= 0
        }

        label.sizeToFit()
        let labelSize = label.text!.sizeWithAttributes([NSFontAttributeName: label.font]) //.size(attributes: [NSFontAttributeName: label.font]) for swift 3

        if(labelSize.width > useWidth){

            let original = label.text!;
            let truncateWidth = useWidth;
            let font = label.font;
            let subLength = label.text!.characters.count

            var temp = label.text!.substringToIndex(label.text!.endIndex.advancedBy(-1)) //label.text!.substring(to: label.text!.index(label.text!.endIndex, offsetBy: -1)) for swift 3
            temp = temp.substringToIndex(temp.startIndex.advancedBy(getTruncatedStringPoint(subLength, original:original, truncatedWidth:truncateWidth, font:font, length:subLength)))
            temp = String.localizedStringWithFormat("%@%@", temp, replacement)

            var count = 0

            while temp.sizeWithAttributes([NSFontAttributeName: label.font]).width > useWidth {

                count+=1

                temp = label.text!.substringToIndex(label.text!.endIndex.advancedBy(-(1+count)))
                temp = temp.stringByTrimmingCharactersInSet(NSCharacterSet.whitespaceCharacterSet()) //remove this if you want to keep whitespace on the end
                temp = String.localizedStringWithFormat("%@%@", temp, replacement)
            }

            label.text = temp;
            label.frame = origSize;
            return true;
        }
        else {

            label.frame = origSize;
            return false
        }
    }

    class func getTruncatedStringPoint(splitPoint:Int, original:String, truncatedWidth:CGFloat, font:UIFont, length:Int) -> Int {

        let splitLeft = original.substringToIndex(original.startIndex.advancedBy(splitPoint))

        let subLength = length/2

        if(subLength <= 0){
            return splitPoint
        }

        let width = splitLeft.sizeWithAttributes([NSFontAttributeName: font]).width

        if(width > truncatedWidth) {
            return getTruncatedStringPoint(splitPoint - subLength, original: original, truncatedWidth: truncatedWidth, font: font, length: subLength)
        }
        else if (width < truncatedWidth) {
            return getTruncatedStringPoint(splitPoint + subLength, original: original, truncatedWidth: truncatedWidth, font: font, length: subLength)
        }
        else {
            return splitPoint
        }
    }
}

Objective C

+ (bool) replaceElipsesForLabel:(UILabel*) label With:(NSString*) replacement MaxWidth:(float) width 

class:

//=============================================Header=====================================================
#import <Foundation/Foundation.h>
#import <UIKit/UIKit.h>

@interface CustomTruncater : NSObject

+ (bool) replaceElipsesForLabel:(UILabel*) label With:(NSString*) replacement MaxWidth:(float) width;

@end

//========================================================================================================

#import "CustomTruncater.h"

@implementation CustomTruncater

static NSString *original;
static float truncateWidth;
static UIFont *font;
static int subLength;

+ (bool) replaceElipsesForLabel:(UILabel*) label With:(NSString*) replacement MaxWidth:(float) width {

CGRect origSize = label.frame;

float useWidth = width;

if(width <= 0)
    useWidth = origSize.size.width; //use label width by default if width <= 0

[label sizeToFit];
CGSize labelSize = [label.text sizeWithFont:label.font];

if(labelSize.width > useWidth) {

    original = label.text;
    truncateWidth = useWidth;
    font = label.font;
    subLength = label.text.length;

    NSString *temp = [label.text substringToIndex:label.text.length-1];
    temp = [temp substringToIndex:[self getTruncatedStringPoint:subLength]];
    temp = [NSString stringWithFormat:@"%@%@", temp, replacement];

    int count = 0;

    while([temp sizeWithFont:label.font].width > useWidth){

        count++;

        temp = [label.text substringToIndex:(label.text.length-(1+count))];
        temp = [temp stringByTrimmingCharactersInSet:[NSCharacterSet whitespaceCharacterSet]]; //remove this if you want to keep whitespace on the end
        temp = [NSString stringWithFormat:@"%@%@", temp, replacement];
    }

    label.text = temp;
    label.frame = origSize;
    return true;
}
else {
    label.frame = origSize;
    return false;
}
}

+ (int) getTruncatedStringPoint:(int) splitPoint {

NSString *splitLeft = [original substringToIndex:splitPoint];
subLength /= 2;

if(subLength <= 0)
    return splitPoint;

if([splitLeft sizeWithFont:font].width > truncateWidth){
    return [self getTruncatedStringPoint:(splitPoint - subLength)];
}
else if ([splitLeft sizeWithFont:font].width < truncateWidth) {
    return [self getTruncatedStringPoint:(splitPoint + subLength)];
}
else {
    return splitPoint;
}
}

@end