Goppinath
Goppinath

Reputation: 10739

How to achieve something like NSLineBreakByTruncatingHead for UITextField?

I need to achieve something exactly like NSLineBreakByTruncatingHead for UITextField as shown here. Let's assume the original text is:

This is the long text that cannot be shown inside a UITextField

I need it like:

...cannot be shown inside a UITextField

but currently I am getting something like:

This is the long text that cannot...

simply the truncation at the beginning. The lineBreakMode property is not given for UITextField. How can I achieve it?

Upvotes: 3

Views: 1164

Answers (2)

anoop4real
anoop4real

Reputation: 7718

I had a similar requirement, I wrote a Swift version of @Stonz2 solution, it worked most of the times, havent used in production yet as the requirement was removed later... anyways posting it here

    extension String {

    func stringByTruncatingLeadingForWidth(width: CGFloat, withFont font: UIFont) -> String{

        var modifiedString = self
        var mutableWidth = width
        let ellipsis = "..."
        if (self.widthOfString(usingFont: font) > width) {
            let ellipsisWidth = ellipsis.widthOfString(usingFont: font)

            // else this will go for infinite loop...mutable width will go -ve
            if mutableWidth > ellipsisWidth {
                mutableWidth -= ellipsis.widthOfString(usingFont: font)
            }
            let range = NSMakeRange(0, 1)

            while modifiedString.widthOfString(usingFont: font) > mutableWidth {
                modifiedString.deleteCharactersInRange(range: range)
                print(modifiedString)
                print(mutableWidth)
            }
            guard let swiftRange = Range(NSMakeRange(0, 3), in: modifiedString) else { return ""  }
            modifiedString.replaceSubrange(swiftRange, with: [".",".","."])
        }
        return modifiedString
    }
    func widthOfString(usingFont font: UIFont) -> CGFloat {
        let fontAttributes = [NSAttributedString.Key.font: font]
        let size = self.size(withAttributes: fontAttributes)
        return size.width
    }

    mutating func deleteCharactersInRange(range: NSRange) {
        guard let swiftRange = Range(range, in: self) else { return  }
        self.removeSubrange(swiftRange)
    }
}

var str1 = "Hello how are you"

let newStr = str1.stringByTruncatingLeadingForWidth(width: 100, withFont: .systemFont(ofSize: 15))

Upvotes: 0

Stonz2
Stonz2

Reputation: 6396

I took the solution here and modified it to truncate the head of a string instead of the tail. Know that it only shows the ellipsis when the field is not being edited.

NOTE: This solution is for iOS 7+ only. To use in iOS 6, use sizeWithFont: instead of sizeWithAttributes: in the NSString+TruncateToWidth.m file.
EDIT: Added support for iOS 6

NSString+TruncateToWidth.h

@interface NSString (TruncateToWidth)
- (NSString*)stringByTruncatingToWidth:(CGFloat)width withFont:(UIFont *)font;
@end

NSString+TruncateToWidth.m

#import "NSString+TruncateToWidth.h"

#define ellipsis @"…"

@implementation NSString (TruncateToWidth)

- (NSString*)stringByTruncatingToWidth:(CGFloat)width withFont:(UIFont *)font
{
    // Create copy that will be the returned result
    NSMutableString *truncatedString = [self mutableCopy];

    // Make sure string is longer than requested width
    if ([self widthWithFont:font] > width)
    {
        // Accommodate for ellipsis we'll tack on the beginning
        width -= [ellipsis widthWithFont:font];

        // Get range for first character in string
        NSRange range = {0, 1};

        // Loop, deleting characters until string fits within width
        while ([truncatedString widthWithFont:font] > width)
        {
            // Delete character at beginning
            [truncatedString deleteCharactersInRange:range];
        }

        // Append ellipsis
        [truncatedString replaceCharactersInRange:NSMakeRange(0, 0) withString:ellipsis];
    }

    return truncatedString;
}

- (CGFloat)widthWithFont:(UIFont *)font
{
    if([self respondsToSelector:@selector(sizeWithAttributes:)])
        return [self sizeWithAttributes:@{NSFontAttributeName:font}].width;
    return [self sizeWithFont:font].width;
}

Using it:

...
// Make sure to import the header file where you want to use it
// assumes instance variable holds your string that populates the field
fieldString = @"abcdefghijklmnopqrstuvwxyz1234567890";
// Size will need to be less than text field's width to account for padding
_myTextField.text = [fieldString stringByTruncatingToWidth:(_myTextField.frame.size.width - 15) withFont:_myTextField.font];
...

// use textFieldShouldBeginEditing to make it animate from the start of the field to the end of the string if you prefer that. I found it a little distracting
- (void)textFieldDidBeginEditing:(UITextField *)textField
{
    textField.text = fieldString;
}

- (BOOL)textFieldShouldEndEditing:(UITextField *)textField
{
    fieldString = textField.text;
    textField.text = [textField.text stringByTruncatingToWidth:(textField.frame.size.width - 15) withFont:textField.font];

    return YES;
}

Upvotes: 3

Related Questions