I am creating a calculator app that has a backspace button just incase the user accidentally typed in a number. Is there a way to remove the last thing that was sent to the UILabel?
sure, I am adding my code to show what I am currently doing,
- (IBAction)digitPressed:(UIButton *)sender {
NSString *digit = sender.currentTitle;
if ([digit isEqualToString:#"."] && !_decimalPressed){
self.display.text = [self.display.text stringByAppendingString:digit];
self.historyDisplay.text = [self.display.text stringByAppendingString:digit];
self.decimalPressed = YES;
}
else if (!_isUserInTheMiddleOfEnteringANumber){
self.display.text = digit;
self.historyDisplay.text = digit;
self.isUserInTheMiddleOfEnteringANumber = YES;
}
else if (![digit isEqualToString:#"."]){
self.display.text = [self.display.text stringByAppendingString:digit];
self.historyDisplay.text = [self.display.text stringByAppendingString:digit];
}
}
This how I am trying to get delete the last element
- (IBAction)backSpace:(id)sender {
}
Just remove the last character from the string set inside your label. Example code:
int numChars = myLabel.text.length;
NSString* truncatedString = [myLabel.text substringToIndex:(numChars - 1)];
mLabel.text = truncatedString;
NSMutableString * stringToModify = [[NSMutableString alloc] initWithString: self.display.text];
if(stringToModify && ([stringToModify length] > 0))
{
[stringToModify deleteCharactersInRange: NSMakeRange([stringToModify length] - 1, 1)];
self.display.text = stringToModify;
[stringToModify release]; // if and only if NOT using ARC
}
- (IBAction)backPressed:(id)sender {
self.display.text=[self.display.text substringToIndex:[self.display.text length] -1];
if ([self.display.text isEqualToString:#""] || [self.display.text isEqualToString:#"-"]){
self.display.text = #"0";
self.userIsInTheMiddleOfEnteringANumber=NO;
}
}
Added in some common logic into the code. If you reach the last number and back space again, it will return the display text as 0. Since it's a calculator, assuming you have created the negative sign ( +/- ), if you are left with -, you will want to delete it and place it with 0 as well. Therefore if everything is relinquish back to 0, you will want to set your userIsInTheMiddleOfEnteringANumber to NO as well.
Related
I have a NSTextView that is displaying what I would call a "rolling log". New AttributedString's are being added just about every second. What I would like to do is truncate from the beginning of the NSTextView if the string has hit a certain length, or a certain number of lines. This is so that the displayed log doesn't take up a ton of memory.
How should I best go about this? I have some code though it doesn't appear to be working as I would expect, specifically around the auto scrolling.
Expected behavior:
Remove leading lines if needed (I don't really care if this is lines or number of characters, whichever is easiest).
Auto-scroll to the bottom if the view isn't scrolled up (so if the user has currently scrolled up, they're not auto-scrolled to the bottom).
The code:
- (void)append:(TextTag*)text toTextView:(MyNSTextView *) textView {
dispatch_async(dispatch_get_main_queue(), ^{
NSAttributedString *attr = [self stringFromTag:text];
NSScroller *scroller = [[textView enclosingScrollView] verticalScroller];
double autoScrollToleranceLineCount = 3.0;
NSUInteger lines = [self countLines:[textView string]];
double scrolled = [scroller doubleValue];
double scrollDiff = 1.0 - scrolled;
double percentScrolled = autoScrollToleranceLineCount / lines;
BOOL shouldScrollToBottom = scrollDiff <= percentScrolled;
[textView.textStorage beginEditing];
if (lines >= 10000) {
NSRange removeRange = [self getRemovalRange:textView.string];
[textView.textStorage deleteCharactersInRange:removeRange];
}
[[textView textStorage] appendAttributedString:attr];
[textView.textStorage endEditing];
if(shouldScrollToBottom) {
[textView scrollRangeToVisible:NSMakeRange([[textView string] length], 0)];
}
});
}
- (NSRange)getRemovalRange:(NSString *)s {
NSUInteger numberOfLines, index, stringLength = [s length];
for (index = 0, numberOfLines = 0; index < stringLength;
numberOfLines++) {
index = NSMaxRange([s lineRangeForRange:NSMakeRange(index, 0)]);
if (numberOfLines >= 100) {
break;
}
}
return NSMakeRange(0, index);
}
- (NSUInteger) countLines:(NSString *)s {
NSUInteger numberOfLines, index, stringLength = [s length];
for (index = 0, numberOfLines = 0; index < stringLength;
numberOfLines++) {
index = NSMaxRange([s lineRangeForRange:NSMakeRange(index, 0)]);
}
return numberOfLines;
}
Here's what I did (years ago, trial and error).
- (void)scrollProgressTextViewToEnd
{
if ([progressTextView isFlipped])
[progressTextView scrollPoint:NSMakePoint(0.0, NSMaxY([progressTextView frame]) - NSHeight([progressTextView visibleRect]))];
else
[progressTextView scrollPoint:NSMakePoint(0.0, 0.0)];
}
- (void)appendToProgressText:(NSString *)theString bold:(BOOL)theBold
{
[progressTextView.textStorage beginEditing];
[self appendToProgressText:theString bold:theBold];
[progressTextView.textStorage endEditing];
[progressTextView didChangeText];
[self performSelector:#selector(scrollProgressTextViewToEnd) withObject:nil afterDelay:0];
}
Method appendToProgressText adds theString to progressTextView.textStorage and doesn't use progressTextView.
Suppose I have the following string:
Mary had a little lamb, she also had a little sheep.
My goal is to extract every word after had and before the period. (In this case a little sheep).
I tried this way:
- (NSInteger)indexOf:(NSString*)substring from:(NSInteger)starts {
NSRange r;
r.location = starts;
r.length = [self length] - r.location;
NSRange index = [self rangeOfString:substring options:NSLiteralSearch range:r];
if (index.location == NSNotFound) {
return -1;
}
return index.location + index.length;
}
As in:
NSInteger sheepSpot = [string indexOf:#"had" from:23];
// I know that I want to grab everything after the index of sheepSpot but before the period.
// Suppose now that I have an arbitrary number of periods in the sentence, how can I extract the above text without getting the wrong thing?
Try this one:
-(NSRange)lastRangeOf:(NSString *)substring inString:(NSString *)string{
return [string rangeOfString:substring options:NSBackwardsSearch];
}
- (void)applicationDidFinishLaunching:(NSNotification *)aNotification{
NSString *string=#"had Mary had a little lamb, she also had a had little sheep.";
NSString *word=#"had";
NSRange hadRange=[self lastRangeOf:word inString:string];
NSInteger start=hadRange.location+word.length;
NSInteger lengthToCut=string.length-start;
NSString *substring=[string substringWithRange:NSMakeRange(start,lengthToCut)];
NSLog(#"->%#",substring);
}
This code will find the last "had" and the last period and give you everything in between:
NSString *text = #"Mary had a little lamb, she also had a little sheep.";
NSString *subtext = nil;
NSRange lastHadRange = [text rangeOfString:#"had" options:NSBackwardsSearch];
if (lastHadRange.location != NSNotFound) {
NSRange lastPeriodRange = [text rangeOfString:#"." options:NSBackwardsSearch];
if (lastPeriodRange.location != NSNotFound) {
NSUInteger start = lastHadRange.location + lastHadRange.length;
NSUInteger length = lastPeriodRange.location - start;
subtext = [text substringWithRange:NSMakeRange(start, length)];
}
}
NSLog(#"Subtext is: %#", subtext);
I am working on an ARC based project. I am obtaining text from a large text file and need to remove white spaces or newline characters from it. The following code works fine on the simulator but crashes on an iPad and doesn't run completely (this may be a memory issues). For example if the loop needs to be run 2000 times, it crashes after running 1800 times on iPad.
- (BOOL)formatTheText {
NSString *content = [NSString stringWithContentsOfURL:_textFileURL
encoding:NSUTF8StringEncoding
error:NULL];
NSRange paraRange = {0,1};
NSString *modifiedContent = #"";
BOOL previousLineWasEmpty = NO;
int lineNumber = 0;
while (paraRange.location < [content length]) {
NSRange currentParaRange = [content paragraphRangeForRange:paraRange];
NSString *paragraph = [content substringWithRange:currentParaRange];
NSCharacterSet *newLineSet = [NSCharacterSet newlineCharacterSet];
NSArray *array = [paragraph componentsSeparatedByCharactersInSet:newLineSet];
NSString *currentParagraph = #"";
for (NSString *line in array) {
currentParagraph = [currentParagraph stringByAppendingString:line];
}
// Add a space when combining two lines
modifiedContent = [modifiedContent stringByAppendingFormat:#"%# ",currentParagraph];
paraRange.location += currentParaRange.length;
if ([currentParagraph length] == 0) {
// If previous line was empty just add a new line character
if (previousLineWasEmpty) {
modifiedContent = [modifiedContent stringByAppendingString:#"\n"];
} else {
// Add two lines for the start of a new paragraph
modifiedContent = [modifiedContent stringByAppendingString:#"\n\n"];
}
previousLineWasEmpty = YES;
} else {
previousLineWasEmpty = NO;
}
lineNumber++;
}
self.cleanedString = modifiedContent;
return YES;
}
I've written the following method to find out whether a long word contains a shorter word, and the order in which I pass the letters appears to effect the outcome.
I've noticed that if I feed it absconds and bassy it correctly reports NO, but if I alphabetize the letters and give it abcdnoss and abssy, it gives YES. I'm not too sure why this is – can anyone spot the issue?
- (BOOL) does: (NSString* ) longWord contain: (NSString *) shortWord {
while([longWord length] > 0 && [shortWord length] > 0) {
NSCharacterSet *set = [NSCharacterSet characterSetWithCharactersInString: [shortWord substringToIndex: 1]];
if ([longWord rangeOfCharacterFromSet: set].location == NSNotFound) {
return NO;
}
longWord = [longWord substringFromIndex: [longWord rangeOfCharacterFromSet: set].location+1];
shortWord = [shortWord substringFromIndex: 1];
}
return YES;
}
The problem with your algorithm is that this line doesn't work:
longWord = [longWord substringFromIndex: [longWord rangeOfCharacterFromSet: set].location+1];
If the first letter you search is at the end of the long word, then long word becomes an empty string, and you jump out of your loop to YES.
I would use a different algorithm, like this. I think it's easier to see what's going on, and so less prone to errors:
- (BOOL) does: (NSString* ) longWord contain: (NSString *) shortWord {
NSMutableString *longer = [longWord mutableCopy];
for (int i = 0; i<shortWord.length; i++) {
NSString *letter = [shortWord substringWithRange:NSMakeRange(i, 1)];
NSRange letterRange = [longer rangeOfString:letter];
if (letterRange.location != NSNotFound) {
[longer deleteCharactersInRange:letterRange];
}else{
return NO;
}
}
return YES;
}
- (BOOL) does: (NSString* ) longWord contain: (NSString *) shortWord
{
return ([longWord rangeOfString:shortWord].location != NSNotFound);
}
I am customizing UItextField for local currency symbol and comma, using following link :
http://www.thepensiveprogrammer.com/2010/03/customizing-uitextfield-formatting-for.html
NSNumber *actualNumber = [currencyFormatter numberFromString:[mstring
stringByReplacingOccurrencesOfString:localeSeparator withString:#""]];
In iOS 5 this actual number is always null and in iOS 4.x it is working fine
My code's main method for this purpose is :
-(BOOL)textField:(UITextField *)textField shouldChangeCharactersInRange:(NSRange)range replacementString:(NSString *)string
{
if (textField.tag == 1)
{
if(true)
{
NSMutableString* mstring = [[textField text] mutableCopy];
if([mstring length] == 0)
{
//special case...nothing in the field yet, so set a currency symbol first
[mstring appendString:[[NSLocale currentLocale] objectForKey:NSLocaleCurrencySymbol]];
//now append the replacement string
[mstring appendString:string];
}
else
{
//adding a char or deleting?
if([string length] > 0)
{
[mstring insertString:string atIndex:range.location];
}
else
{
//delete case - the length of replacement string is zero for a delete
[mstring deleteCharactersInRange:range];
}
}
NSString* localeSeparator = [[NSLocale currentLocale]
objectForKey:NSLocaleGroupingSeparator];
NSNumber *actualNumber = [currencyFormatter numberFromString:[mstring
stringByReplacingOccurrencesOfString:localeSeparator
withString:#""]];
NSLog(#"%#",actualNumber);
[textField setText:[currencyFormatter stringFromNumber:actualNumber]];
[mstring release];
}
//always return no since we are manually changing the text field
return NO;
}
else
{
return YES;
}
}
and This is the initialization
NSLocale *paklocal = [[[NSLocale alloc] initWithLocaleIdentifier:#"en_PAK"] autorelease];
currencyFormatter = [[NSNumberFormatter alloc] init];
[currencyFormatter setFormatterBehavior: NSNumberFormatterBehavior10_4];
[currencyFormatter setNumberStyle:NSNumberFormatterCurrencyStyle];
[currencyFormatter setMaximumFractionDigits:0];
[currencyFormatter setLocale:paklocal];
NSMutableCharacterSet *numberSet = [[NSCharacterSet decimalDigitCharacterSet] mutableCopy];
[numberSet formUnionWithCharacterSet:[NSCharacterSet whitespaceCharacterSet]];
nonNumberSet = [[numberSet invertedSet] retain];
[numberSet release];
I think you're having a problem because textField:shouldChangeCharactersInRange:replacementString: adds the currency symbol for [NSLocale currentLocale], which may be different from the locale used by currencyFormatter. In the simulator on my computer, it added $ (dollar) signs, to mstring, which were logically enough rejected by currencyFormatter.
When you construct paklocal, store it along with currencyFormatter and use it instead of [NSLocale currentLocale].
If you have further trouble with currencyFormatter, use NSLog to display the string you send into it.