UISegmentedControl method to set Segment Index? - objective-c

I'm using a UISegmentedControl in a view to select a specific client. This is then setting an NSString property in my data model from the unique segment title as and when the view is closed. All works exactly as I had hoped. When I reload the view depending on what is stored in the model, I am then setting the UISegmentedControl with the following code in viewDidLoad. i.e. it reads the string property from the model, converts it to an index and selects the correct segment to reflect which client is stored in the model.
if ([self.itemToEdit.client isEqualToString:#"John"]) {
myIndex = 3;
} else if ([self.itemToEdit.client isEqualToString:#"David"]) {
myIndex = 2;
} else if ([self.itemToEdit.client isEqualToString:#"Paul"]) {
myIndex = 1;
} else if ([self.itemToEdit.client isEqualToString:#"Stephen"]) {
myIndex = 0;
}
self.reportEditorClient.selectedSegmentIndex = myIndex;
All works as planned, it's just that it seems quite clunky. I have scoured the documentation to see if there is a UISegmentedControl method that will do this but cannot find anything. Is there a better approach, or am I on the right lines here?

Put this name-to-index mapping in a dictionary, then this chain of if-else pretty much becomes a one liner.

Code formatting wise, you could use an array of names
NSArray *names = [NSArray arrayWithObjects:#"John", #"David", #"Paul", #"Stephen", nil];
then
self.reportEditorClient.selectedSegmentIndex = [names indexOfObject:self.itemToEdit.client];
(Though it's only slightly neater)

Related

Returning NSTextView's Selection Attributes

When you use TextEdit and have a selection of string, it will give you the selection color, font, size and other attributes as you see above. How do you get those text selection attributes? I'm certain that I need to use the selectedTextAttributes method. I have the following lines of code.
- (void)textViewDidChangeSelection:(NSNotification *)notification {
if ([notification object] == textView1) {
...
...
NSMutableDictionary *dict = [[textView1 selectedTextAttributes] mutableCopy];
NSLog(#"%#",dict);
}
}
If I run it, the result is not quite like what I expect.
NSBackgroundColor = "NSNamedColorSpace System selectedTextBackgroundColor";
NSColor = "NSNamedColorSpace System selectedTextColor";
There aren't really useful values that I can use to get the text color of the string selection and other attributes. If I ask Google about selectedTextColor, I don't get much luck.
Thank you for your help.
selectedTextAttributes describe what the selection highlighting looks like, not the attributes of selected text. I looked for quite some time for the answer to this question, and finally found it here:
Attribute String Programming Guide
Some example code. For an NSTextView* named editingView, this gathers an array of NSDictionary objects for all the differently formatted ranges in the selection.
NSMutableArray* attributes = [NSMutableArray array];
NSRange selRange = editingView.selectedRange;
NSRange effectiveRange = NSMakeRange(selRange.location, 0);
while (NSMaxRange(effectiveRange) < NSMaxRange(selRange)) {
[attributes addObject: [editingView.textStorage attributesAtIndex: NSMaxRange(effectiveRange) longestEffectiveRange: &effectiveRange inRange: selRange]];
}

What is the most efficient way to move NSArray objects to UITextFields?

I have some code where there may or may not be objects in the Array... this is the code I am dealing with:
oServices1.text = CustomServicesArray[0];
oServices2.text = CustomServicesArray[1];
oServices3.text = CustomServicesArray[2];
oServices4.text = CustomServicesArray[3];
oServices5.text = CustomServicesArray[4];
oServices6.text = CustomServicesArray[5];
oServices7.text = CustomServicesArray[6];
oServices8.text = CustomServicesArray[7];
oServices9.text = CustomServicesArray[8];
oServices10.text = CustomServicesArray[9];
oServices11.text = CustomServicesArray[10];
oServices12.text = CustomServicesArray[11];
oServices13.text = CustomServicesArray[12];
oServices14.text = CustomServicesArray[13];
oServices15.text = CustomServicesArray[14];
oServices16.text = CustomServicesArray[15];
oServices17.text = CustomServicesArray[16];
oServices18.text = CustomServicesArray[17];
oServices19.text = CustomServicesArray[18];
oServices20.text = CustomServicesArray[19];
oServices21.text = CustomServicesArray[20];
oServices22.text = CustomServicesArray[21];
oServices23.text = CustomServicesArray[22];
Rather than check each and every array object for nil, is there a way I can take the oServices*xx*.text UIFields and put them into some kind of array so I can just use a loop?
Are you aware of reflexivity? With KVC you could save up much code and time:
for(int i=1; i<=23; i++) {
NSString* key= [NSString stringWithFormat: #"oServices%d"i];
// Remember that variables should start with a lowercase letter
[[self valueForKey: key] setText: customServicesArray[i-1] ];
}
But if you don't want to bind all these variables in your storyboard/xib file (even this may be too much), just set the tag of each text field in the order that you want (from 1), so that you can get them back using viewWithTag:
// From the UIViewController
for(int i=1; i<=23; i++) { // Consider defining a constant instead of 23
[[self.view viewWithTag: i] setText: customServicesArray[i-1] ];
}
I consider this last solution better because you avoid binding so many variables.
You can use an OutletCollection to hold oServices and loop on that. Note however that outlet collections are not sorted so you would need to sort them beforehand (on the tag criteria, or location for example).
For ordering see this question.
Set the tag property of the UITextFields to their corresponding ordinal in the array. The default value of tag is 0, so you may need to set the tag property to ordinal + 1 if there are other views in the parent view of your UITextFields. On the parent view of your text fields, you can use the viewWithTag: method to retrieve the appropriate UITextField.

How to do an on-item-changed for an NSPopUpButton?

I'm trying to implement a system that changes a label based on the state of an NSPopUpButton.
So far I've tried to do what's displayed in the code below, but whenever I run it, the code just jumps into the else clause, throwing an alert
- (IBAction)itemChanged:(id)sender {
if([typePopUp.stringValue isEqualToString: #"Price per character"]) {
_currency = [currencyField stringValue];
[additionalLabel setStringValue: _currency];
}
else if([typePopUp.stringValue isEqualToString: #"Percent saved"]) {
_currency = additionalLabel.stringValue = #"%";
}
else alert(#"Error", #"Please select a calculation type!");
}
So does anyone here know what to do to fix this?
#hamstergene is on the right track, but is comparing the title of the menu item rather than, say, the tag, which is wrong for the following reasons:
It means you cannot internationalize the app.
It introduces the possibility of spelling mistakes.
It's an inefficient comparison; comparing every character in a string takes way longer than comparing a single integer value.
Having said all that, NSPopUpButton makes it difficult to insert tags into the menu items, so you need to use the index of the selected item:
Assume you create the menu items using:
[typePopUp removeAllItems];
[typePopUp addItemsWithTitles: [NSArray arrayWithObjects: #"Choose one...", #"Price per character", #"Percent saved", nil]];
Then create an enum that matches the order of the titles in the array:
typedef enum {
ItemChooseOne,
ItemPricePerCharacter,
ItemPercentSaved
} ItemIndexes;
And then compare the selected item index, as follows:
- (IBAction)itemChanged:(id)sender {
NSInteger index = [(NSPopUpButton *)sender indexOfSelectedItem];
switch (index) {
case ItemChooseOne:
// something here
break;
case ItemPricePerCharacter:
_currency = [currencyField stringValue];
[additionalLabel setStringValue: _currency];
break;
case ItemPercentSaved:
_currency = #"%"; // See NOTE, below
additionalLabel.stringValue = #"%";
break;
default:
alert(#"Error", #"Please select a calculation type!");
}
}
NOTE the following line was incorrect in your code:
_currency = additionalLabel.stringValue = #"%";
Multiple assignment works because the result of x = y is y. This is not the case when a setter is involved. The corrected code is above.
EDIT This answer was heavily edited following more info from the OP.
To query the title of currently selected item in NSPopUpButton:
NSMenuItem* selectedItem = [typePopUp selectedItem];
NSString* selectedItemTitle = [selectedItem title];
if ([selectedItemTitle isEqualTo: ... ]) { ... }
Note that comparing UI strings is a very bad idea. A slightest change in UI will immediately break your code, and you are preventing future localization. You should assign numeric or object values to each item using -[NSMenuItem setTag:] or -[NSMenuItem setRepresentedObject:] and use them to identify items instead.

NSString to class instance variable

I am looking for a way to convert from NSString to a class instance variable. For sample code below, say filter is "colorFilter". I want filternameclassinstancegohere to be replaced with colorFilter.
- (void)filterSelected:(NSString *)filter
{
self.filternameclassinstancegohere = ….;
}
While there were good suggested solutions given for this question, I discovered what I needed is the NSClassFromString method. Here is a final implementation:
- (void)filterSelected:(NSString *)filter
{
//self.filternameclassinstancegohere = ….;
self.myViewController = [[NSClassFromString(filter) alloc] initWithNibName:filter bundle:nil];
}
Consider using one NSMutableDictionary instance variable with string keys rather than 40 instance variables.
You can create an arbitrary selector using NSSelectorFromString():
SEL methodName = NSSelectorFromString(filter);
[self performSelector:methodName];
This will call a method colorFilter in your example above.
Would be wise to check with respondsToSelector before calling, too.
If the filter value can only be a small, constant number of things, just use an enumeration and a switch statement:
enum Filter
{
ColorFilter,
FooFilter,
BarFilter
};
- (void)filterSelected:(Filter)filter
{
switch(filter)
{
case ColorFilter:
self.colorFilter = ...;
break;
case FooFilter:
self.fooFilter = ...;
break;
case BarFilter:
self.barFilter = ...;
break;
}
}
If the set of filter values is large and could change frequently, then you could also use Key-Value Coding. It's more complicated but more flexible.

interacting with UIViews stored inside a NSMutableArray

a big noob needs help understanding things.
I have three UIViews stored inside a NSMutableArray
lanes = [[NSMutableArray arrayWithCapacity:3] retain];
- (void)registerLane:(Lane*)lane {
NSLog (#"registering lane:%i",lane);
[lanes addObject:lane];
}
in the NSLog I see: registering lane:89183264
The value displayed in the NSLog (89183264) is what I am after.
I'd like to be able to save that number in a variable to be able to reuse it elsewhere in the code.
The closest I could come up with was this:
NSString *lane0 = [lanes objectAtIndex:0];
NSString *description0 = [lane0 description];
NSLog (#"description0:%#",description0);
The problem is that description0 gets the whole UIView object, not just the single number (dec 89183264 is hex 0x550d420)
description0's content:
description0:<Lane: 0x550d420; frame = (127 0; 66 460); alpha = 0.5; opaque = NO; autoresize = RM+BM; tag = 2; layer = <CALayer: 0x550d350>>
what I don't get is why I get the correct decimal value with with NSLog so easily, but seem to be unable to get it out of the NSMutableArray any other way. I am sure I am missing some "basic knowledge" here, and I would appreciate if someone could take the time and explain what's going on here so I can finally move on. it's been a long day studying.
why can't I save the 89183264 number easily with something like:
NSInteger * mylane = lane.id;
or
NSInteger * mylane = lane;
thank you all
I'm really confused as to why you want to save the memory location of the view? Because that's what your '89183264' number is. It's the location of the pointer. When you are calling:
NSLog (#"registering lane:%i",lane);
...do you get what's actually being printed out there? What the number that's being printed means?
It seems like a really bad idea, especially when if you're subclassing UIView you've already got a lovely .tag property which you can assign an int of your choosing.
You're making life infinitely more complex than it needs to be. Just use a pointer. Say I have an array containing lots of UIViews:
UIView *viewToCompare = [myArray objectAtIndex:3];
for (id object in myArray) {
if (object == viewToCompare) {
NSLog(#"Found it!");
}
}
That does what you're trying to do - it compares two pointers - and doesn't need any faffing around with ints, etc.