How to know the length of NSString that fits a UILabel with fixed size? - iphone

I know NSString has methods that determine the frame size for it, using NSString UIKit Additions, sizeWithFont......
How about the other way around? I mean if I have a fixed frame size, how do I know how many characters or words for a NSString that can fit into it?
If I know this, I can cut off the NSString easily.
thanks

It might not be the most elegant solution, but you could do something like this:
- (NSString *)string:(NSString *)sourceString reducedToWidth:(CGFloat)width withFont:(UIFont *)font {
if ([sourceString sizeWithFont:font].width <= width)
return sourceString;
NSMutableString *string = [NSMutableString string];
for (NSInteger i = 0; i < [sourceString length]; i++) {
[string appendString:[sourceString substringWithRange:NSMakeRange(i, 1)]];
if ([string sizeWithFont:font].width > width) {
if ([string length] == 1)
return nil;
[string deleteCharactersInRange:NSMakeRange(i, 1)];
break;
}
}
return string;
}
Then call it like this:
NSString *test = #"Hello, World!";
CGFloat width = 40.0;
UIFont *font = [UIFont systemFontOfSize:[UIFont labelFontSize]];
NSString *reducedString = [self string:test reducedToWidth:width withFont:font];
NSLog(#"%#", reducedString);

You cannot know/determine the number of characters that fits in a UILabel with fixed width because some characters are smaler than others, eg l and m.
There are two options:
Use Mono-Space-Fonts (each character has also a fixed width). Then determine the width for one char in your font with your font-size and calculate the number of chars
Allow any number of characters and check on insert if the inserted characters fit.
You have to know what behaviour you want to have. What should happen if there is text that does not fit. If you only want to truncate (like the solution of mortenfast does) then just use UILineBreakModeTailTruncation for the lineBreakMode-property of your UILabel (there are more options, like TruncateHead, Clip, Word Wrap)

Or you just just use the lineBreak property and set it to NSLineBreakByCharWrapping and move on with your life. https://stackoverflow.com/a/29088337/951349

Thanks #Morten. I've updated the sample code to handle word separation. It also eliminates extra spaces in between words. It has not been tested in the field, but my tests have, thus far, proven OK. Please update at your leisure if you find improvements or bug/glitch fixes.
-(NSString*)string:(NSString*)sourceString reducedToWidth:(CGFloat)width withFont:(UIFont*)font {
// if full string is within bounds, simply return the full string
if( [sourceString sizeWithFont:font].width <= width ) return sourceString;
// break up string into words. if <= 1 word, return original string
NSArray* words = [sourceString componentsSeparatedByString:#" "];
NSInteger numWords = [words count];
if( numWords <= 1 ) return sourceString;
// our return var. we populate as we go
NSMutableString* str = [NSMutableString string];
// temp var to test with before adding to return string
NSMutableString* strTemp = [NSMutableString string];
// string to hold word LESS spaces
NSString* strWordTemp = nil;
// the word we're currently on
NSInteger numWord = 0;
// whether we need to add a space (when not last word)
Boolean addSpace = NO;
// loop through our words....
for( NSString* strWord in words ) {
// which word we're on
numWord++;
// eliminate white space
strWordTemp = [strWord stringByReplacingOccurrencesOfString:#" " withString:#""];
// if this word is empty or was a space(s), skip it
if( [strWordTemp isEqualToString:#""] ) continue;
// append to temp string
[strTemp appendString:strWordTemp];
// if we're still within the bounds...
if( [strTemp sizeWithFont:font].width <= width ) {
// default = no extra space
addSpace = NO;
// if we're not the last word, add a space & check for length
if( numWord < numWords ) {
[strTemp appendString:#" "];
// if adding space made it too long, then just don't add it!
if( [strTemp sizeWithFont:font].width > width ) {
// it was too long with space, so we'll just add word
[str appendString:strWordTemp];
break;
}
// otherwise, it's OK to add the space
else addSpace = YES;
}
// append to return string and continue
[str appendFormat:#"%#%#", strWordTemp, ( addSpace ? #" " : #"" )];
}
// otherwise, we're done
else break;
}
// return our result
return str;
}

Related

ios method to insert spaces in string

In my app I download a file from amazon's s3, which does not work unless the file name has no spaces in it. For example, one of the files is "HoleByNature". I would like to display this to the user as "Hole By Nature", even though the file name will still have no spaces in it.
I was thinking of writing a method to search through the string starting at the 1st character (not the 0th) and every time I find a capital letter I create a new string with a substring until that index with a space and a substring until the rest.
So I have two questions.
If I use NSString's characterAtIndex, how do I know if that character is capital or not?
Is there a better way to do this?
Thank you!
Works for all unicode uppercase and titlecase letters
- (NSString*) spaceUppercase:(NSString*) text {
NSCharacterSet *set = [NSCharacterSet uppercaseLetterCharacterSet];
NSMutableString *result = [NSMutableString new];
for (int i = 0; i < [text length]; i++) {
unichar c = [text characterAtIndex:i];
if ([set characterIsMember:c] && i!=0){
[result appendFormat:#" %C",c];
} else {
[result appendFormat:#"%C",c];
}
}
return result;
}
I would not go to that approach because I know you can download files with spaces try this please when you construct the NSUrl object
#"my_web_site_url\sub_domain\sub_folder\My%20File.txt
this will download "My File.txt" from the URL provided. so basically you can replace all spaces in the URL with %20
reference:
http://www.w3schools.com/tags/ref_urlencode.asp
Got it working with Jano's answer but using the isupper function as suggested by Richard J. Ross III.
- (NSString*) spaceUppercase:(NSString*) text
{
NSMutableString *result = [NSMutableString new];
[result appendFormat:#"%C",[text characterAtIndex:0]];
for (int i = 1; i < [text length]; i++)
{
unichar c = [text characterAtIndex:i];
if (isupper(c))
{
[result appendFormat:#" %C",c];
}
else
{
[result appendFormat:#"%C",c];
}
}
return result;
}

Convert string fraction to decimal

I have a plist file that I am reading out a measurement, but some of the measurements are fractions such as 6 3/8". I formatted them that way because it's easier to find that on a tape measure than it is to find 6.375". My problem is now I want to do a conversion to metric on the fly and it isn't reading in the fraction part of the number. My current code is this.
cutoutLabel.text = [NSString stringWithFormat:#"%.2f mm. %#", [[[sizeDict valueForKey:Sub_Size] objectForKey:#"Cutout Dimensions"]floatValue] * 25.4, [temp objectAtIndex:2]];
Thanks.
That's what I ended up doing.
NSArray *temp = [[[sizeDict valueForKey:Sub_Size] objectForKey:#"Cutout Dimensions"] componentsSeparatedByString:#" "];
if ([temp count] > 2) {
NSArray *fraction = [[temp objectAtIndex:1]componentsSeparatedByString:#"/"];
convertedFraction = [[fraction objectAtIndex:0]floatValue]/[[fraction objectAtIndex:1]floatValue];
}
You can get the numerator and denominator as follows:
NSRange slashPos = [fraction.text rangeOfString:#"/"];
NSString * numerator = [fraction.text substringToIndex:slashPos.location];
NSString * denominator = [fraction.text substringFromIndex:slashPos.location+1];
You should take more care than this,
check that your range is of length 1 and make sure that the string has characters after the "/" character. But if you know you are feeding this code a fraction string it should work in your case
The idea is in place, but you will also need to apply the same logic first to separate the whole number from you fraction. Apply the same logic, searching for a #" " and then find the numerator and denominator
Building on Ian's answer and trying to be a little more complete (since his example was for a whole number and fractional part with an inch character (6 3/8"), I suggest the following method (it also works if there are spaces before the whole number:
// Convert a string consisting of a whole and fractional value into a decimal number
-(float) getFloatValueFromString: (NSString *) stringValue {
// The input string value has a format similar to 2 1/4". Need to remove the inch (") character and
// everything after it.
NSRange found = [stringValue rangeOfString: #"\""]; // look for the occurrence of the " character
if (found.location != NSNotFound) {
// There is a " character. Get the substring up to the "\"" character
stringValue = [stringValue substringToIndex: found.location];
}
// Now the input format looks something like 2 1/4. Need to convert this to a float value
NSArray *temp = [stringValue componentsSeparatedByString:#" "];
float convertedFraction = 0;
float wholeNumber = 0;
for (int i=0; i<[temp count]; i++) {
if ([[temp objectAtIndex:i] isEqualToString:#""]) {
continue;
}
NSArray *fraction = [[temp objectAtIndex:i]componentsSeparatedByString:#"/"];
if ([fraction count] > 1) {
convertedFraction = [[fraction objectAtIndex:0]floatValue]/[[fraction objectAtIndex:1]floatValue];
}
else if ([fraction count] == 1) {
wholeNumber = [[fraction objectAtIndex:0] floatValue];
}
}
convertedFraction += wholeNumber;
return convertedFraction;
}

iPhone SDK: How to manipulate a txtField

Trying to manipulate the contents of a txtField in the textFieldDidEndEditing. What we want to do is rotate the entire contents left and then append the current character at the end.
This is to allow a user to enter a money value without a decimal point.
This is what we have so far but I'm not sure this is the best way to approach this. Nor is this code working as expected.
Any help appreciated.
//easier input for total
if (textField == txtGrandTotal)
{
//copy contents of total to string2
NSString *string1 = txtGrandTotal.text;
NSMutableString *string2;
//now string2 contains the buffer to manipulate
string2 = [NSMutableString stringWithString: string1];
//so, copy it to strMoney2
strMoney2 = string2;
int charIndex;
//for all character in strMoney2, move them rotated left to strMoney1
for (charIndex = 0; charIndex < [strMoney2 length]; charIndex++)
{
NSString *strChar = [strMoney2 substringWithRange: NSMakeRange(charIndex, 1)];
[strMoney1 insertString:strChar atIndex:charIndex+1];
}
//now append the current character to strMoney1
NSString *strCurrentChar = [string1 substringWithRange: NSMakeRange([string1 length], 1)];
[strMoney1 appendString:strCurrentChar];
//move manipulated string back to txtGrandTotal
txtGrandTotal.text = strMoney1;
}
Out of a zillion ways to approach this, this is quite short:
NSString *res = [NSString stringWithFormat:#"%#%#",
[input substringFromIndex:1],
[input substringToIndex:1]
];
or shorter:
NSString *res = [[input substringFromIndex:1]
stringByAppendingString:[input substringToIndex:1]];

stringByReplacingCharactersInRange don't replace It's Append !

I Spent 5 hours try to figure a way for that..i'm trying to do a hangman app for iphone and the method below is the method that should be called when the player chooses a character and it match the chosen word..
-(void)replaceTheHiddenTextWithNewText:(NSString*)character{
NSString *fullTextField = fullText.text;
int textCount = [hiddenText.text length];
NSString *theRiddle;
for (int i = textCount-1 ; i>=0; i--) {
NSString *hiddenTextField = [[NSMutableString alloc] initWithString:hiddenText.text];
NSString *aChar=[fullTextField substringWithRange:NSMakeRange(i/3,1)];
if ([aChar isEqualToString:#" "]) {
theRiddle= [hiddenTextField stringByReplacingCharactersInRange:NSMakeRange(i, 1) withString:#" "];
}else if ([aChar isEqualToString:character]) {
theRiddle =[hiddenTextField stringByReplacingCharactersInRange:NSMakeRange(i, 1) withString:aChar];
}else{
theRiddle = [hiddenTextField stringByReplacingCharactersInRange:NSMakeRange(i, 1) withString:#"_"];
}
hiddenTextField = theRiddle;
}
hiddenText.text=theRiddle;
}
the problem is stringByReplacingCharactersInRange doesn't replace the character, it appends it to the underscore what am I doing wrong here?
Best Regards,
M Hegab
Just played around with your code. It does not work, but stringByReplacingCharactersInRange is not your problem.
Your game logic doesn't work like it should. Get a pen and a sheet of paper and "manually" loop through your for loop to see that this must be wrong.
Next time, if you've stared at code for half an hour, take a pen. This will save you at least 4 hours :-)
There are some issues with your code. Assume Kartoffelkäfer is the word you are looking for, and the user enters the letter f.
for (int i = textCount-1 ; i>=0; i--) {
NSString *hiddenTextField = [[NSMutableString alloc] initWithString:hiddenText.text];
// you are creating this string in every loop from the text of a (I guess) UITextField.
// I don't know what the content of this text is but I guess it is suppossed to be `______________`
// in every loop you replace the word where you replaced the _ with the correct letter with the string from the textfield.
// Btw, you are leaking this string.
NSString *aChar=[fullTextField substringWithRange:NSMakeRange(i/3,1)];
// Kartoffelkäfer has 14 chars so i is 13. And 13/3 is 4. And the character at index 4 is o
// In the next loop i is 12. And 12/3 is 4, too.
// next three loops will give you index 3. Then you get three times index 2, and so one.
// you never reach the letter f, anyway.
if ([aChar isEqualToString:#" "]) {
theRiddle= [hiddenTextField stringByReplacingCharactersInRange:NSMakeRange(i, 1) withString:#" "];
}else if ([aChar isEqualToString:character]) {
theRiddle =[hiddenTextField stringByReplacingCharactersInRange:NSMakeRange(i, 1) withString:aChar];
}else{
theRiddle = [hiddenTextField stringByReplacingCharactersInRange:NSMakeRange(i, 1) withString:#"_"];
// You should not replace a unmatched character with a _ . Because already matched letters would be overwritten.
}
hiddenTextField = theRiddle;
}
I assumed that the content of hiddenText.text is #"______"
and the content of fullText.text is #"Kartoffelkäfer". So hiddentext is the exact length as the fullText.
What I had to change to get this to work:
NSString *theRiddle;
NSString *hiddenTextField = [[[NSMutableString alloc] initWithString:hiddenText.text] autorelease];
for (int i = textCount-1 ; i>=0; i--) {
NSString *aChar=[fullTextField substringWithRange:NSMakeRange(i,1)];
if ([aChar isEqualToString:#" "]) {
theRiddle= [hiddenTextField stringByReplacingCharactersInRange:NSMakeRange(i, 1) withString:#" "];
}else if ([aChar isEqualToString:character]) {
theRiddle =[hiddenTextField stringByReplacingCharactersInRange:NSMakeRange(i, 1) withString:aChar];
}
else {
theRiddle = hiddenTextField;
}
hiddenTextField = theRiddle;
}
hiddenText.text=theRiddle;
Far away from good code, but I tried to change your code as little as possible.

iphone string initial char before space

I have a question...
I wish take from a string that contains a name and surname, the initial of the first and the surname complete....
example:
NSString* myName = #"Mel Gibson";
//I Wish have "M Gibson";
NSString* myName2 = #"Leonardo Di Caprio";
//I wish have "L Di Caprio";
Thanks
#implementation NSString (AbbreviateFirstWord)
-(NSString*)stringByAbbreviatingFirstWord {
// step 1: Locate the white space.
NSRange whiteSpaceLoc = [self rangeOfString:#" "];
if (whiteSpaceLoc.location == NSNotFound)
return self;
// step 2: Remove all characters between the first letter and the white space.
NSRange rangeToRemove = NSMakeRange(1, whiteSpaceLoc.location - 1);
return [self stringByReplacingCharactersInRange:rangeToRemove withString:#""];
}
#end