Based on number of lines in uitextview getting substring - iphone

Frrom UITextview i am able to get number of lines for given text, if its more than 4 lines i want to truncate the rest, so how to get the index to substring?

It will restrict to user to enter more than 4 lines in a UITextview
- (BOOL)textView:(UITextView *)aTextView shouldChangeTextInRange:(NSRange)aRange replacementText:(NSString*)aText
{
if (textView.contentSize.height/textView.font.lineHeight>4) {
return NO;
}
else
return YES;
}

NSMutableString *str = [[NSMutableString alloc] initWithString:textView.text];
static int numberOfLines = 0;
int nOC = 1;
while (nOC < [textView.text count] && numberOfLines < 4) {
if ([[str substringWithRange:NSMakeRange(nOC,1)] isEqualToString:#"\n"]; ) {
numberOfLines++;
}
nOC++;
}
NSString *finalString = [str substringWithRange:NSMakeRange(1,nOC)];
i hope this should work out for u. i calculated the number of characters till the 4th "\n" and used substringWithRange to extract the desired string. i didnt try this piece of code but this logic should work or at least assist u in your code. happy coding :)

Related

UILabel - Display ... when text length crosses certain length

I have placed UILabel in my application, in that I want to display the text with .... once the length of the text exceeds the certain count.
Because if the text goes longer, it gives the design issue.
Please let know which function to use.
Try this
yourLabel.lineBreakMode=UILineBreakModeTailTruncation;
If you are adding your UILabel from interface builder you can do it directly. Select you UILabel and in the Utilities column in Attriubtes Inspector=> Label section=> Line Breaks set Truncate Tail
Try this will helpful for you.
NSString *string=YourString;
int size=[YourString length];
if (size>21)
{
NSMutableString *string1 = [[NSMutableString alloc]init];
char c;
for(int index = 0;index <20 ;index++)
{
c =[string characterAtIndex:index];
[string1 appendFormat:#"%c",c];
}
[string1 appendFormat:#"..."];
string=string1;
}
Add "string" on your UILable.
#define EXCEEDED_LENGTH 8
- (NSString *) checkStringLength:(NSString *)str
{
if(str.length >= EXCEEDED_LENGTH)
{
return [NSString stringWithFormat:#"%#...",[str subStringToIndex:EXCEEDED_LENGTH-1]];
}
return str;
}
yourLabel.text = [self checkStringLength:#"Hello World !!"];
Output like Hello Wo... For better output you can trim whitespaces before pass string to function.
From the information you shared I think the autoshrink and linebreakermode may be the root cause.IT is the property which tries to show the contents in the specified frame which will decrease and adjust the font size
2 ways to sove the issue
adjust the property according to requirement
Increase the framesize of label(programmatically by finding size)
also look on the edge insets
Either you can make UILable size(length/width) Dynamic,
Or
You can UITextView with edit disable so if there will be long text it will be scrollable.
CGSize constraint = CGSizeMake(690.0, 2000.0);
CGSize size_txt_overview1 = [[headItemArray objectAtIndex:k] sizeWithFont:[UIFont fontWithName:#"Helvetica" size:18] constrainedToSize:constraint lineBreakMode:UILineBreakModeWordWrap];
UILabel *lbl_headitem = [[UILabel alloc]initWithFrame:CGRectMake(3,h, 690, size_txt_overview1.height)];
lbl_headitem.numberOfLines=0;
Please set your UILabel width and height in CGSize constraint. Best way.. and it worked for me.
This is a method for a Category of UILabel
-(void)setTruncatedTextWithDotsIfNeeded:(NSString *)text
{
float fullTextWidth = [text sizeWithFont:self.font].width;
float labelWidth = self.frame.size.width;
if(fullTextWidth<=labelWidth){
[self setText:text];
return;
}
NSString *dots = #"…";
float dotsWidth = [dots sizeWithFont:self.font].width;
NSRange fullRange = [text rangeOfString:text];
for(int i = fullRange.length; i >= fullRange.location; i--){
NSRange currentRange;
currentRange.location = 0;
currentRange.length = i;
NSString *partialText = [text substringWithRange:currentRange];
float partialTextWidth = [partialText sizeWithFont:self.font].width;
if(partialTextWidth + dotsWidth <= labelWidth){
[self setText:[NSString stringWithFormat:#"%#...",partialText]];
return;
}
}
}

Go to particular line in UITextView

How can I tell UITextView to set the cursor to a particular line (line 13, for example) when it appears?
UITextView has a method setSelectedRange: (NSRange) range.
If you know where in the string line 13 occurs, say location 237, then do: [textView setSelectedRange: NSMakeRange(237,0)];
If you need to find out where line 13 occurs, you've more work ahead. I'd start by looking at sizeWithFont, remembering to nip about 16 points off the width of your textView so that iOS gets the sums right. (That said, if you have line breaks, then just find the location of the 13th (or nth) "\n".)
Update following your further query in the comment
There are lots of ways of finding the position of the nth \n. The following snippet isn't pretty, but it will do the job. You could also use rangeOfString and iterate through the "\n". In this snippet, if the target line is greater than the number of lines, it puts the cursor at the end. The code here assumes you have a UITextView property called userEntry.
int targetLine = 3; // change this as appropriate 0=first line
NSRange range;
NSString* exampleString = #"Hello there\nHow is it going?\nAre you looking for a new line?\nA new line in what?\nThat remains to be seen";
NSArray* separateLines = [exampleString componentsSeparatedByString:#"\n"];
if (targetLine < [separateLines count])
{
int count = 0;
for (int i=0; i<targetLine; i++)
{
count = count + [[separateLines objectAtIndex:i] length] + 1; // add 1 to compensate \n separator
}
range = NSMakeRange(count, 0);
}
else
{
range = NSMakeRange([exampleString length], 0); // set to the very end if targetLine is > number of lines
}
[[self userEntry] setText: exampleString];
[[self userEntry] setSelectedRange:range];
[[self userEntry] becomeFirstResponder];

How to know the displayed text in UILabel?

I have an UIView containing two UILabels, in order to display a string.
The first UILabel has a fixed size, and if the string is too long and can't hold in this UILabel, I want to display the maximum characters I can in the first UILabel, and display the rest of the string in the second UILabel.
But to make this, I must know the exact part of the string displayed in the first UILabel, which is not easy because of the randomness of the string and the linebreaks.
So, is there a way to get just the text displayed in the first UILabel, without the truncated part of the string?
if ([_infoMedia.description length] > 270) {
NSRange labelLimit = [_infoMedia.description rangeOfString:#" " options:NSCaseInsensitiveSearch range:NSMakeRange(270, (_infoMedia.description.length - 270))];
_descTop.text = [_infoMedia.description substringToIndex:labelLimit.location];
_descBottom.text = [_infoMedia.description substringFromIndex:(labelLimit.location+1)];
} else {
_descTop.text = _infoMedia.description;
_descBottom.text = #"";
}
Okay that's a late answer but maybe it could help someone. The code above is approximatively the solution I used in my app.
_descTop is my first label and _descBottom is the second label. 270 is a constant equivalent to a little less than the average maximum number of characters displayed in my first label, _descTop. I calculated it by hand, trying with many different strings, maybe there's a better way to do that but this worked not bad.
If the string I want to display (_infoMedia.description) is larger than 270 characters, I isolate the first 270 characters plus the end of the next word in the string (by searching the next space), in the case where the 270 characters limit would cut the string in the middle of a word. Then I put the first part of the string in my first label, and the second part in the second label.
If not, I only put the globality of the string in the first label.
I know that's a crappy solution, but it worked and I didn't found any better way to do that.
Following code might help you in getting what you want!!
//If you want the string displayed in any given rect, use the following code..
#implementation NSString (displayedString)
//font- font of the text to be displayed
//size - Size in which we are displaying the text
-(NSString *) displayedString:(CGSize)size font:(UIFont *)font
{
NSString *written = #"";
int i = 0;
int currentWidth = 0;
NSString *nextSetOfString = #"";
while (1)
{
NSRange range;
range.location = i;
range.length = 1;
NSString *nextChar = [self substringWithRange:range];
nextSetOfString = [nextSetOfString stringByAppendingString:nextChar];
CGSize requiredSize = [nextSetOfString sizeWithFont:font constrainedToSize:CGSizeMake(NSIntegerMax, NSIntegerMax)];
currentWidth = requiredSize.width;
if(size.width >= currentWidth && size.height >= requiredSize.height)
{
written = [written stringByAppendingString:nextChar];
}
else
{
break;
}
i++;
}
return written;
}
#end

Loop through labels iPhone SDK

Ok I have 8 labels and I want to loop through them but am having no luck.
This is what I have tried.
for (int i; i = 0; i < 10; i++)
{
double va = [varible1.text doubleValue] + i;
int j = 0 + I
label(j).text= [[NSString alloc]initWithFormat:#"%2.1f", va];
}
This errors out. My labels are named like this label0, label1, label2
Any help would be appreciated.
label(j) is NOT equivalent to label0, label1, etc.
You should create an NSArray of labels, then you can access them with [arrayOfLabels objectAtIndex:j]. If you're not sure what this means, please read the documentation about NSArray...
You should maybe add all your labels to a C array, probably in -viewDidLoad
UILabel* labels[] = { label0, label1, label2, ... };
(not entirely sure about the syntax)
and then access them like
labels[i].text = ...
By the way, I think you're leaking memory here:
labels[i].text = [[NSString alloc]initWithFormat:#"%2.1f", va];
initWithFormat: will return a string with a retain count of 1. labels[i].text will retain that value again. You should release the string after setting the label's text. I'd probably just autorelease it here:
labels[i].text = [[[NSString alloc]initWithFormat:#"%2.1f", va] autorelease];
or use stringWithFormat (which returns an autoreleased string):
labels[i].text = [NSString stringWithFormat:#"%2.1f", va];
for (UILabel *lbl in self.view.subviews)
{
[lbl setFont:[UIFont fontWithName:#"AppleGothic" size:22]];
}
it will change all the labels in your ViewController by just giving tags to labels.
If you cannot or do not want to put your labels in an array, you could iterate through the UIViews using the tag field as an index. You store the index numbers in them (either through IB or programatically) and then get each label using: (UIView *)viewWithTag:(NSInteger)tag.
See below (set theView to the view your labels reside in):
for (int i; i = 0; i < 10; i++)
{
double va = [varible1.text doubleValue] + i;
UILabel * label = [theView viewWithTag: i];
label.text= [[NSString alloc]initWithFormat:#"%2.1f", va];
}

What is the best way to enter numeric values with decimal points?

In my app users need to be able to enter numeric values with decimal places. The iPhone doesn't provides a keyboard that's specific for this purpose - only a number pad and a keyboard with numbers and symbols.
Is there an easy way to use the latter and prevent any non-numeric input from being entered without having to regex the final result?
Thanks!
I think it would be good to point out that as of iOS 4.1 you can use the new UIKeyboardTypeDecimalPad.
So now you just have to:
myTextField.keyboardType=UIKeyboardTypeDecimalPad;
A more elegant solution happens to also be the simplest.
You don't need a decimal separator key
Why? Because you can simply infer it from the user's input. For instance, in the US locale when you what to enter in $1.23, you start by entering the numbers 1-2-3 (in that order). In the system, as each character is entered, this would be recognized as:
user enters 1: $0.01
user enters 2: $0.12
user enters 3: $1.23
Notice how we inferred the decimal separator based on the user's input. Now, if the user wants to enter in $1.00, they would simply enter the numbers 1-0-0.
In order for your code to handle currencies of a different locale, you need to get the maximum fraction digits of the currency. This can be done with the following code snippet:
NSNumberFormatter *currencyFormatter = [[NSNumberFormatter alloc] init];
[currencyFormatter setNumberStyle:NSNumberFormatterCurrencyStyle];
int currencyScale = [currencyFormatter maximumFractionDigits];
For example, the Japanese yen has a maximum fraction digit of 0. So, when dealing with yen input, there is no decimal separator and thus no need to even worry about fractional amounts.
This approach to the problem allows you to use the stock numeric input keypad provided by Apple without the headaches of custom keypads, regex validation, etc.
Here is an example for the solution suggested in the accepted answer. This doesn't handle other currencies or anything - in my case I only needed support for dollars, no matter what the locale/currency so this was OK for me:
-(BOOL)textField:(UITextField *)textField shouldChangeCharactersInRange:(NSRange)range
replacementString:(NSString *)string {
double currentValue = [textField.text doubleValue];
//Replace line above with this
//double currentValue = [[textField text] substringFromIndex:1] doubleValue];
double cents = round(currentValue * 100.0f);
if ([string length]) {
for (size_t i = 0; i < [string length]; i++) {
unichar c = [string characterAtIndex:i];
if (isnumber(c)) {
cents *= 10;
cents += c - '0';
}
}
} else {
// back Space
cents = floor(cents / 10);
}
textField.text = [NSString stringWithFormat:#"%.2f", cents / 100.0f];
//Add this line
//[textField setText:[NSString stringWithFormat:#"$%#",[textField text]]];
return NO;
}
The rounds and floors are important a) because of the floating-point representation sometimes losing .00001 or whatever and b) the format string rounding up any precision we deleted in the backspace part.
I wanted to do exactly the same thing, except with currencies rather than straight decimal values.
I ended up creating a custom view which contains a UITextField and a UILabel. The label covers the text field, but the text field still receives touches. I use the UITextFieldTextDidChange notification to observe changes in the text field (and I used a NSNumberFormatter to turn the resulting number into a formatted currency value) to update the label.
To disable the loupe that allows the user to reposition the insertion point, you'll need to use a custom UITextField subclass and override touchesBegan:withEvent: and set it to do nothing.
My solution might be different from what you need because the decimal point is always fixed -- I use the system's currency setting to determine how many there digits ought to be after the decimal point. However, the numeric keypad doesn't have a decimal point on it. And you can't add any buttons to the keyboard (which is especially aggravating because there's a blank button in the lower-left corner of the keyboard that would be perfect for a decimal point!) So I don't have a solution for that, unfortunately.
Depending on the specific application, providing a slider that the user can select a position from might be a better choice on the iphone. Then no digits need to be entered at all.
You may want to use a slider (as suggested by Martin v. Löwis) or a UIPickerView with a separate wheel for each of the digits.
I built a custom Number pad view controller with decimal point... check it out:
http://sites.google.com/site/psychopupnet/iphone-sdk/tenkeypadcustom10-keypadwithdecimal
As of iOS4.1, there is a new keyboard type UIKeyboardTypeNumberPad, but unfortunately, as of yet it doesn't seem to appear in the Interface Builder pick list.
Here's how to do it without using floats, round() or ceil() in a currency agnostic manner.
In you view controller, set up the following instance variables (with associated #property statements if that's your bag):
#interface MyViewController : UIViewController <UITextFieldDelegate> {
#private
UITextField *firstResponder;
NSNumberFormatter *formatter;
NSInteger currencyScale;
NSString *enteredDigits;
}
#property (nonatomic, readwrite, assign) UITextField *firstResponder;
#property (nonatomic, readwrite, retain) NSNumberFormatter *formatter;
#property (nonatomic, readwrite, retain) NSString *enteredDigits;
#end
and your viewDidLoad method should contain the following:
- (void)viewDidLoad {
[super viewDidLoad];
NSNumberFormatter *aFormatter = [[NSNumberFormatter alloc] init];
[aFormatter setNumberStyle:NSNumberFormatterCurrencyStyle];
currencyScale = -1 * [aFormatter maximumFractionDigits];
self.formatter = aFormatter;
[aFormatter release];
}
Then implement your UITextFieldDelegate methods as follows:
#pragma mark -
#pragma mark UITextFieldDelegate methods
- (void)textFieldDidBeginEditing:(UITextField *)textField {
// Keep a pointer to the field, so we can resign it from a toolbar
self.firstResponder = textField;
self.enteredDigits = #"";
}
- (void)textFieldDidEndEditing:(UITextField *)textField {
if ([self.enteredDigits length] > 0) {
// Get the amount
NSDecimalNumber *result = [[NSDecimalNumber decimalNumberWithString:self.enteredDigits] decimalNumberByMultiplyingByPowerOf10:currencyScale];
NSLog(#"result: %#", result);
}
}
- (BOOL)textField:(UITextField *)textField shouldChangeCharactersInRange:(NSRange)range replacementString:(NSString *)string {
// Check the length of the string
if ([string length]) {
self.enteredDigits = [self.enteredDigits stringByAppendingFormat:#"%d", [string integerValue]];
} else {
// This is a backspace
NSUInteger len = [self.enteredDigits length];
if (len > 1) {
self.enteredDigits = [self.enteredDigits substringWithRange:NSMakeRange(0, len - 1)];
} else {
self.enteredDigits = #"";
}
}
NSDecimalNumber *decimal = nil;
if ( ![self.enteredDigits isEqualToString:#""]) {
decimal = [[NSDecimalNumber decimalNumberWithString:self.enteredDigits] decimalNumberByMultiplyingByPowerOf10:currencyScale];
} else {
decimal = [NSDecimalNumber zero];
}
// Replace the text with the localized decimal number
textField.text = [self.formatter stringFromNumber:decimal];
return NO;
}
Only tested this with pounds and pence, but it should work with Japanese Yen too. If you want to format decimals for non-currency purposes, then just read the documentation on NSNumberFormatter and set whatever format/maximumFractionDigits you want.
A Swift 2 implementation of Mike Weller's post, also only USD:
func textField(textField: UITextField, shouldChangeCharactersInRange range: NSRange, replacementString string: String) -> Bool {
guard let str = textField.text else {
textField.text = "0.00"
return false
}
let value = (str as NSString).doubleValue
var cents = round(value * 100)
if string.characters.count > 0 {
for c in string.characters {
if let num = Int(String(c)) {
cents *= 10
cents += Double(num)
}
}
}
else {
cents = floor(cents / 10)
}
textField.text = NSString(format: "%.2f", cents/100) as String
return false
}
You can use STATextField and set currencyRepresentation to YES which:
Ensures no more than one decimal point is entered and that no more than 2 digits are entered to the right of said decimal point.
There's also STAATMTextField which supports currency mode and ATM text entry by default:
Provides a text field that mimics ATM machine input behavior.