Custom Formatter Weird Behavior - objective-c

I have a custom NSFormatter linked to 4 NSTextFields. When I change the values of my text fields manually, everything works fine. But when I change it through a combo box. I get an error looking like this:
-[__NSCFNumber length]: unrecognized selector sent to instance 0xc0c3
I've noticed that the application continuously sends this error and that the instance is always the same (0xc0c3). Also, when my NSTextFields aren't linked to my custom formatter, everything works well, even through the combo box.
Do you guys know what may be the source of the problem?
Thanks in advance!
Here's some code:
Combo box action:
- (void)subnetMaskByNumberOfSubnetBits:(id)sender{
// ------- Sets the subnet mask when the user selects the number of bits
NSNumberFormatter *stringToNumber = [[NSNumberFormatter alloc] init];//TURN A STRING INTO A NUMBER
NSNumber *selectedAmountOfBits = [[NSNumber alloc] init];//CONTAINS THE SELECTED NUMBER OF BITS
selectedAmountOfBits = [stringToNumber numberFromString:[sender objectValueOfSelectedItem]];
[self changeSubnetMaskUsingNumberOfMaskBits:selectedAmountOfBits];
//RELEASE
[stringToNumber release];
}
-(void)changeSubnetMaskUsingNumberOfMaskBits:(NSNumber *)numberOfMaskBitsSelected{
// --------- Change the subnet mask based on the number of bits
NSInteger numberOfFullOctets;
NSInteger valueOfLastOctet;
NSInteger octetCounter;
NSMutableDictionary *subnetMaskFields = [[NSMutableDictionary alloc] init];
//Contains keys to all the outlets
[subnetMaskFields setObject:subnetMaskOctet1 forKey:#"subnetMaskField1"];
[subnetMaskFields setObject:subnetMaskOctet2 forKey:#"subnetMaskField2"];
[subnetMaskFields setObject:subnetMaskOctet3 forKey:#"subnetMaskField3"];
[subnetMaskFields setObject:subnetMaskOctet4 forKey:#"subnetMaskField4"];
//NUMBER OF FULL OCTETS AND VALUE OF LAST OCTET
numberOfFullOctets = [numberOfMaskBitsSelected intValue]/8;
valueOfLastOctet = 256 - pow(2, 8 - ([numberOfMaskBitsSelected intValue] - (8 * ([numberOfMaskBitsSelected intValue]/8)))); //Big complicated formula
//-------Setting the fields------//
//SETTING THE FIELDS OF FULL OCTETS
for (octetCounter = 1; octetCounter <= numberOfFullOctets; octetCounter++) {
[[subnetMaskFields objectForKey:[NSString stringWithFormat:#"subnetMaskField%i", octetCounter]] setStringValue:#"255"];
}
//SETTING THE FIELD OF THE INCOMPLETE OCTET
[[subnetMaskFields objectForKey:[NSString stringWithFormat:#"subnetMaskField%i", octetCounter]] setIntegerValue:valueOfLastOctet];
//FILLING THE ZER0S
while (octetCounter < 4) {
octetCounter++;
[[subnetMaskFields objectForKey:[NSString stringWithFormat:#"subnetMaskField%i", octetCounter]] setStringValue:#"0"];
}
//RELEASE
[subnetMaskFields release];
}

The problem is this line of code:
[[subnetMaskFields objectForKey:[NSString stringWithFormat:#"subnetMaskField%i", octetCounter]] setIntegerValue:valueOfLastOctet];
From what I understand, since the NSFormatter needs to get the string value of my text field, I cannot set the text field as an integer. This line of code
[[subnetMaskFields objectForKey:[NSString stringWithFormat:#"subnetMaskField%i", octetCounter]] setStringValue:[NSString stringWithFormat:#"%ld", valueOfLastOctet]];
solves the problem.

Related

NSTextStorage - How to find return chars (new line) in a text

In an NSTextStorage I insert time strings at the current pointer location like this :
NSMutableAttributedString* attrString = [[NSMutableAttributedString alloc] initWithString: #"00:00:00"];
[attrString autorelease];
int pos = [self selectedRange].location;
[[self textStorage] insertAttributedString: attrString atIndex:pos];
So far so good, it works perfect. But now I want to position the pointer at the beginning of the next line. Obviously this is right after the next return char.
Now how to find the next return char in textStorage and position the pointer there ?
I have not found any hint in the web for this task. Please help ...
You won't find a method to set the cursor (more precise: selection with zero length) in NSTextStorage's API, because it is a storage, having no selection. The selection is a property of the text view. This is the result of MVC. Simply do a check: You can have many text views displaying the same text. Obviously each one needs its own selection.
What you have to do is to get the position of the next paragraph (this is better than searching for \n) and set this as a selection for the text view.
NSMutableAttributedString* attrString = [[NSMutableAttributedString alloc] initWithString: #"00:00:00"];
[attrString autorelease]; // You should *really* use ARC
int pos = [self selectedRange].location;
[[self textStorage] insertAttributedString: attrString atIndex:pos];
// Get the next paragraph
NSString *text = self.textStorage.string;
NSRange restOfStringRange = NSMakeRange( pos, [text length]-pos );
NSUInteger nextParagraphIndex;
// You have to look to the start of the next paragraph
[text getParagraphStart:&nextParagraphIndex end:NULL contentsEnd:NULL forRange:restOfStringRange];
NSTextView *view = self.textView; // Or where ever you get it from
view.selectedRange = NSMakeRange(nextParagraphIndex, 0);
Typed in Safari, not tested, just to show the basic approach.

string not populating with array data, even though arrays are not really empty

I am trying to create a non-Document-based application for Mac OS X that randomizes cards for the game of Dominion.
From many of the ones I have tried, the only thing I cannot seem to do is limit the number of sets picked from a selection made by the user, and things worked pretty well in my program, but I am having issues.
I am trying to get the results to print in a custom view, but every time I look at the print preview, nothing shows, except header text, as specified in an NSMutableString.
This piece of code is what is being used to print and is found in MasterViewController:
- (IBAction)print:(id)sender
{
NSMutableString *content = [[NSMutableString alloc] initWithString:#"Cards\r\n\r\n"];
for (int i = 0; i < [supply.game count]; i++)
{
[content appendFormat:#"Card: %# Set: %# Cost: %d\r\n", [supply.game[i] name], [supply.game[i] collection], [supply.game[i] cost]];
}
[content appendFormat:#"\r\n\r\nRequired\r\n\r\n"];
for (int i = 0; i < [[setup supply] count]; i++)
{
NSDictionary* current = [setup supply][i];
NSString* key = [current allKeys][0]; // get the key of the current dictionary must be 0, as there is only one key
int value = [[current valueForKey:key] integerValue]; // variable to hold key value
if (value > 0) {
[content appendFormat:#"%#: %#", key, #"Yes"];
}
else
{
[content appendFormat:#"%#: %#", key, #"No"];
}
}
printView.content = [NSMutableString stringWithString:content];
[printView print:sender];
}
the data initially gets filled into some tableviews, which displays the correct content, and the supply.game array is the exact array that contains cards used for games.
setup is a property that refers to a view controller that populates a table with kinds of cards that may be required for games (e.g. shelters, colonies, ruins, spoils, and potions) and the supply method is supposed to return the array that view controller creates, which is itself not empty, as that table populates properly.
printView is a property that is assigned to a custom view found in MainMenu.xib and is the real view being used to print from.
the printView class looks like this:
header:
#import <Cocoa/Cocoa.h>
#interface PrintView : NSView
{
NSMutableString* content;
}
#property NSMutableString* content;
- (void)drawStringInRect:(NSRect)rect; // method to draw string to page
- (void)print:(id)sender; // method to print
#end
implementation:
#import "PrintView.h"
#implementation PrintView
#synthesize content;
- (BOOL)acceptsFirstResponder
{
return YES;
}
- (void)print:(id)sender
{
[[NSPrintOperation printOperationWithView:self] runOperation];
}
- (void)drawRect:(NSRect)dirtyRect {
NSGraphicsContext *context = [NSGraphicsContext currentContext];
if ([context isDrawingToScreen])
{
}
else
{
[[NSColor whiteColor] set];
NSRect bounds = [self bounds];
if (content == nil || [content length] == 0)
{
NSRectFill(bounds);
}
else
{
[self drawStringInRect:bounds];
}
}
}
- (void)drawStringInRect:(NSRect)rect
{
NSSize strSize; // variable to hold string size
NSPoint strOrigin; // variable used to position text
NSMutableDictionary *attributes = [[NSMutableDictionary alloc] init];
[attributes setObject:[NSFont fontWithName:#"Helvetica" size:12] forKey:NSFontAttributeName];
[attributes setObject:[NSColor blackColor] forKey:NSForegroundColorAttributeName];
strSize = [content sizeWithAttributes:attributes];
strOrigin.x = rect.origin.x + (rect.size.width - strSize.width)/2;
strOrigin.y = rect.origin.y + (rect.size.height - strSize.height)/2;
[content drawAtPoint:strOrigin withAttributes:attributes];
}
#end
When I check the array sizes for printing operation, the size of the arrays is reported as zero, thus resulting in my current problem
If you need more code, here is code from Github, but I do not have the experimental branch up there, which is where the above code came from, though it should not be too different.
The MasterViewController will show how the supply.game array is made and SetupViewController houses the code that is used to determine what is needed in the game, as well as show how the supply array from [setup supply] is being produced.
MasterViewController has also been added as an object to MainMenu.xib, so I do not know if that affects anything.
Any idea of what I need to do?
Edit: Added in info that might be relevant

UILabel Multiple Lines

I have a UILable which displays text as I press buttons. The text is from an attributed string. One of the buttons calls for a superscript attribute:
string = [[NSMutableAttributedString alloc]initWithString:#"A"];
[string addAttribute:NSFontAttributeName value:(font) range:NSMakeRange(string.length-1, 1)];
[string addAttribute:(NSString*)kCTSuperscriptAttributeName value:#"1" range:NSMakeRange(string.length-1, 1)];
[string2 appendSttributedString: string];
label.attributedText = string2;
This code works as long as string2 fits onto one line in the UILable. When the text begins to span two lines at first it appears as it should. However when the kCTSuperscriptAttributeName superscript attribute is added the second line of the label disappears and gets truncated. Im not sure whats going on. Anyone have an idea?
Make sure that you are setting the numberOfLines property on the UILabel to be 2 or something, so that the label doesn't truncate beyond the first line. Hope this helps!
Did you try 'NSBaselineOffsetAttributeName' for NSAttributedString?
-(void)setText:(id)text withPrefixText:(id)prefixText andSuffixText:(id)suffixText
{
NSString * compondText = [self textByCompoundingText:text withPrefixText:prefixText WithsuffixText:suffixText];
NSMutableAttributedString * attributedCompoundText = [[NSMutableAttributedString alloc] initWithString:compondText];
NSMutableDictionary * prefixTextAttributes = [#{} mutableCopy];
prefixTextAttributes[NSFontAttributeName] = _prefixTextFont? _prefixTextFont:self.font;
prefixTextAttributes[NSForegroundColorAttributeName] = _prefixTextColour? _prefixTextColour:self.textColor;
NSNumber *baselineOffSet =[NSNumber numberWithUnsignedInteger:_prefixTextVerticalPositionning];
prefixTextAttributes[(NSString*)NSBaselineOffsetAttributeName] = baselineOffSet;
NSMutableDictionary * suffixTextAttributes = [#{} mutableCopy];
suffixTextAttributes[NSFontAttributeName] = _suffixTextFont? _suffixTextFont:self.font;
suffixTextAttributes[NSForegroundColorAttributeName] = _suffixTextColour ?_suffixTextColour:self.textColor;
baselineOffSet = [NSNumber numberWithUnsignedInteger:_suffixTextVerticalPostioning];
suffixTextAttributes[(NSString*)NSBaselineOffsetAttributeName] = baselineOffSet;
if(prefixText)
[attributedCompoundText addAttributes:prefixTextAttributes range:[compondText rangeOfString:prefixText]];
if(suffixText)
[attributedCompoundText addAttributes:suffixTextAttributes range:[compondText rangeOfString:suffixText]];
self.attributedText = attributedCompoundText;
}

RTL & LTR (bidirectional) in UITextView

I'm trying to save the content of a UITextView which contains lines of text formatted both RTL and LTR.
The problem is that UITextView checks only the first character to format direction. Let's assume I'm in "edit" mode and write this text (__ means spaces):
text1_______________________________________
____________________________________________אקסא
text2_______________________________________
and after saving we lost RTL for אקסא. Now I'd like to edit this text once again which now looks like:
text1_______________________________________
אקסא
text2_______________________________________
I'm not able to mix \u200F with \u200E directional characters in one UITextView.
How to manage this and save correctly bidirectional text from UITextView?
Here is a quick proof of concept using NSAttributedString :
- Split the text in paragraphs
- For each paragraph, detect the main language
- Create an attributed text with the correct alignmenent for the corresponding range
// In a subclass of `UITextView`
+ (UITextAlignment)alignmentForString:(NSString *)astring {
NSArray *rightToLeftLanguages = #[#"ar",#"fa",#"he",#"ur",#"ps",#"sd",#"arc",#"bcc",#"bqi",#"ckb",#"dv",#"glk",#"ku",#"pnb",#"mzn"];
NSString *lang = CFBridgingRelease(CFStringTokenizerCopyBestStringLanguage((CFStringRef)astring,CFRangeMake(0,[astring length])));
if (astring.length) {
if ([rightToLeftLanguages containsObject:lang]) {
return NSTextAlignmentRight;
}
}
return NSTextAlignmentLeft;
}
- (void)setText:(NSString *)str { // Override
[super setText:str];
// Split in paragraph
NSArray *paragraphs = [self.text componentsSeparatedByCharactersInSet:[NSCharacterSet newlineCharacterSet]];
// Attributed string for the whole string
NSMutableAttributedString *attribString = [[NSMutableAttributedString alloc]initWithString:self.text];
NSUInteger loc = 0;
for(NSString *paragraph in paragraphs) {
// Find the correct alignment for this paragraph
NSMutableParagraphStyle *paragraphStyle = [[NSMutableParagraphStyle alloc]init];
[paragraphStyle setAlignment:[WGTextView alignmentForString:paragraph]];
// Find its corresponding range in the string
NSRange range = NSMakeRange(loc, [paragraph length]);
// Add it to the attributed string
[attribString addAttribute:NSParagraphStyleAttributeName value:paragraphStyle range:range];
loc += [paragraph length];
}
[super setAttributedText:attribString];
}
Also, I recommend reading the Unicode BiDi Algorithm to manage more complex use cases.

Obj-C - NSString that saves all input using currentTitle and UILabel.text methods

NSString* digit = [sender currentTitle];
if (self.userIsInTheMiddleOfEnteringANumber){
NSString* currentDisplayText = [[self display]text];
NSString* newDisplayText = [currentDisplayText stringByAppendingString:digit];
self.display.text = newDisplayText;
I´ll try and explain my problem as good as I can. This line of code here takes the digit from a selection of buttons, saves it no the string "digit". "currentDisplayText"takes the digit displayed in a label. Then these two strings are appended and sent to the "display" (label)
But there is also an enter button. Which clears the display (so user can enter new number ) this is a calculator btw!
- (IBAction)enterPressed{
[self.brain pushOperand:[self.display.text doubleValue]];
self.userIsInTheMiddleOfEnteringANumber = NO;
What I want is to display a "history" label that displays all the numbers entered - I have not quite understood how I get the string I save the "history" too, to not reset like the display does. This might be bad exlained by me, but any hints are appreciated. I am still learning objective-c...
If I understood you correctly, you want to save the number after pressing the enter button. For this you need to add an attribute of type NSMutableArray to your class. Then in the method do:
- (IBAction)enterPressed {
[myHistoryArrayAttrib addObject: self.display.text];
[self.brain pushOperand:[self.display.text doubleValue]];
self.userIsInTheMiddleOfEnteringANumber = NO;
//after this you can clear your display
self.display.text = nil;
}
That's it.
I did not understander you very well , but one thing I am sure , if you want save your data (not very large) , you should use NSUserDeafult . use - (void)setObject:(id)anObject forKey:(id)aKey;to save the data.
You want to svae the history , if you want to save the lastest history , you could use - (NSString *)stringForKey:(NSString *)defaultName; to get the string which you save. and if you want to save the whole history , you could use - (NSArray *)arrayForKey:(NSString *)defaultName; to get the array which you save.
give an eg. how to save a string
if([[NSUserDefaults standardUserDefaults] stringForKey:theKey] == nil)
{
NSString *defaultValue = [NSString stringWithString:theStringYouWantedSave];
NSDictionary *saveDict = [NSDictionary dictionaryWithObject:defaultValue forKey:theKey];
[[NSUserDefaults standardUserDefaults] registerDefaults:saveDict];
}
else
{
[[NSUserDefaults standardUserDefaults] setObject:theStringYouWantedSave forKey:theKey];
}
and get like this
if([[NSUserDefaults standardUserDefaults] stringForKey:theKey]!=nil)
{
yourHistoryString = [[NSUserDefaults standardUserDefaults] stringForKey:theKey];
}