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?
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
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
.
Look at -[UILabel setLineBreakMode:]
and UILineBreakModeCharacterWrap
. The default value of -[UILabel lineBreakMode]
is UILineBreakModeTailTruncation
, which causes the ellipsis at the end.
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.
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:
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
© 2022 - 2024 — McMap. All rights reserved.
ellipsis
– Bently