How to change truncate characters in UILabel?
Asked Answered
Z

6

6

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?

Zebedee answered 25/1, 2011 at 12:22 Comment(1)
FYI the three little dots are called an ellipsisBently
S
11

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
Stemson answered 22/2, 2013 at 9:6 Comment(0)
B
0

You can also set

[lbl setAdjustsFontSizeToFitWidth:YES];

With this there will be no need of truncating text and you can display the complete text on your label.

Bhatt answered 25/1, 2011 at 12:54 Comment(1)
does not work because I'm using a custom font (set in UIAppFonts-key)Zebedee
H
0

Look at -[UILabel setLineBreakMode:] and UILineBreakModeCharacterWrap. The default value of -[UILabel lineBreakMode] is UILineBreakModeTailTruncation, which causes the ellipsis at the end.

Haydon answered 24/10, 2011 at 15:11 Comment(0)
G
0

As Javanator said you would have to do your own truncation. You shuld use the sizeWithFont:forWidth:lineBreakMode: message on the UIKit additions to NSString class to get the width of a string with a certain font. This will handle all types of fonts.

Link

Goon answered 13/3, 2012 at 14:5 Comment(0)
T
0

I would like to provide a more Swifty version of what Fonix provided earlier and using Swift 5 syntax. Also I decided to write the functions as an extension of UILabel.

extension UILabel {
    func replaceEllipsis(withString replacement: String, andMaximumWidth width: CGFloat = 0) -> Bool {

        if let labelText = self.text, let font = self.font {
            let origSize = self.frame
            var useWidth = width

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

            self.sizeToFit()
            let labelSize = labelText.size(withAttributes: [NSAttributedString.Key.font: font])

            if labelSize.width > useWidth {
                let truncateWidth = useWidth
                let subLength = labelText.count

                var newText = String(labelText[..<labelText.index(labelText.endIndex, offsetBy: -1)])
                newText = String(newText[..<newText.index(labelText.startIndex, offsetBy: getTruncatedStringPoint(splitPoint: subLength,
                                                                                                                  original: labelText,
                                                                                                                  truncatedWidth: truncateWidth,
                                                                                                                  font: font,
                                                                                                                  length: subLength))])
                newText = String.localizedStringWithFormat("%@%@", newText, replacement)
                var count = 0

                while newText.size(withAttributes: [NSAttributedString.Key.font: font]).width > useWidth {
                    count += 1
                    newText = String(labelText[..<labelText.index(labelText.endIndex, offsetBy: -(1 + count))])
                    newText = newText.trimmingCharacters(in: NSCharacterSet.whitespaces)
                    newText = String.localizedStringWithFormat("%@%@", newText, replacement)
                }
                self.text = newText
                self.frame = origSize
                return true
            } else {
                self.frame = origSize
                return false
            }
        } else {
            return false
        }
    }

    private func getTruncatedStringPoint(splitPoint: Int, original: String, truncatedWidth: CGFloat, font: UIFont, length: Int) -> Int {
        let index = original.index(original.startIndex, offsetBy: splitPoint)
        let splitLeft = String(original[..<index])

        let subLength = length / 2

        if subLength <= 0 {
            return splitPoint
        }

        let width = splitLeft.size(withAttributes: [NSAttributedString.Key.font: font]).width

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

It'll be used as follows:

<UILabel>.replaceEllipsis(withString: " ...Read More") // if you want to use the label width

Also you can pass a custom width as well if you need to. I opted for the default width in the above example.

For references on what I used in my refactor, the below StackOverflow links were helpful:

Advanced by refactor

substringToIndex refactor

Tocology answered 13/2, 2022 at 19:13 Comment(0)
F
-3

why dont you code to count the length of string and makes its substring if its exceeding the view. or do anything you want It is raw but effective method

Fritter answered 25/1, 2011 at 12:25 Comment(1)
i am not saying to make any standard api using this. Font specification will be fixed as per requirement i guess.Fritter

© 2022 - 2024 — McMap. All rights reserved.