Setting Actions for PopUpButton - objective-c

I have created an NSPopUpButton programmatically and I made an array for my choices, how can I create a setAction for each individual array choice? Thanks!
NSRect buttonRect = NSMakeRect(1705, 145, 78, 50);
//Button Array. When I pick the choice it closes the diologue box
NSArray *newArray;
NSString *color1 = #"Blue Color";
NSString *color2 = #"Green Color";
NSString *color3 = #"Clear Color";
newArray = [NSArray arrayWithObjects: color1, color2, color3, nil];
NSPopUpButton *button = [[NSPopUpButton alloc] initWithFrame:buttonRect pullsDown:YES];
[self addSubview:button];
[button addItemsWithTitles:newArray];
//want my action for each individual string
[button setAction:#selector(changeFilterColor)];
-(void) changeFilterColor
{
NSLog(#"colorChanged");
}

You need to add the NSMenuDelegate protocol to your interface (.h file):
#interface MyClass : NSObject <NSMenuDelegate>
Then:
[[button menu]setDelegate:self];
after you create the NSPopUpButton. Also, remove the line with setAction:.
Copy this delegate method:
-(void)menu:(NSMenu *)menu willHighlightItem:(NSMenuItem *)item
{
if ([item.title isEqualToString:#"Blue Color"]) { ... } //etc
}
Add the necessary if statements to complete the comparisons.

Another option for this is the use the setAction method you originally had in your code. But then for the selector you choose to call set it up to receive a sender object. Something like this:
-(void)method:(NSMenuItem *)sender
Then you can verify that the sender object is valid and get the title from that instead. I'm a little wary of using NSMenuDelegate for this.

Related

Subclassing NSPopUpButton to add a bindable property

I'm trying to add a bindable property to a custom NSPopUpButton subclass.
I've created a "selectedKey" property, which is meant to store a NSString associated with selected menu item.
In control init, I set self as button target and an action for the button (valueChanged:), which in turn sets "selectedKey" in accordance with user selection:
#interface MyPopUpButton : NSPopUpButton {
NSMutableDictionary *_items;
NSString *_selectedKey;
}
#property(nonatomic, readwrite, copy) NSString* selectedKey;
- (void)addItemWithTitle:(NSString *)title andKey:(NSString *)key;
#end
#implementation MyPopUpButton
- (instancetype)initWithFrame:(NSRect)frameRect {
self = [super initWithFrame:frameRect];
if (self) {
_items = [NSMutableDictionary new];
[NSObject exposeBinding:#"selectedKey"];
[super setTarget:self];
[super setAction:#selector(valueChanged:)];
}
return self;
}
- (void)addItemWithTitle:(NSString *)title andKey:(NSString *)key {
[super addItemWithTitle:title];
[_items setValue:title forKey:key];
}
- (void)valueChanged:(id)sender {
for (NSString *aKey in [_items allKeys]) {
if ([[_items valueForKey:aKey] isEqualToString:[self titleOfSelectedItem]]) {
self.selectedKey = aKey;
}
}
}
- (void)setSelectedKey:(NSString *)selectedKey {
[self willChangeValueForKey:#"selectedKey"];
_selectedKey = selectedKey;
[self didChangeValueForKey:#"selectedKey"];
[self selectItemWithTitle:[_items valueForKey:selectedKey]];
}
#end
This seems to work as expected: "selectedKey" property is changed when user changes PopUpButton selection.
Unfortunately, trying to bind this property, doesn't work.
[selectButton bind:#"selectedKey" toObject:savingDictionary withKeyPath:key options:#{NSContinuouslyUpdatesValueBindingOption : #YES }]
When selection is changed bind object is not updated accordingly.
What am I doing wrong?
I've created a "selectedKey" property, which is meant to store an NSString associated with selected menu item.
Bindings is definitely the way to go here, but your use of bind:toObject:withKeyPath:options is incorrect.
The value that you pass to the first argument must be one of the predefined values made available by Apple for that particular control. For NSPopUpButton objects, the available values are documented in the NSPopUpButton Bindings Reference. When you look through this document you'll see that there is no selectedKey option. There is however a selectedValue which has the following description:
An NSString that specifies the title of the selected item in the NSPopUpButton.
Thus the correct way to set up the binding is as follows:
[self.btn bind:#"selectedValue"
toObject:self
withKeyPath:#"mySelectedString"
options:nil];
This is all you need to do: when the action selector is fired the property stored at the keyPath you passed in as the third argument will already have been updated. This means that you can (i) get rid of the setSelectedKey method entirely, (ii) remove exposeBinding line, and (iii) remove the code within valueChanged: - Cocoa has already done this bit.
The example below implements just two methods, but, if I've understood your intentions, they should be all you need:
- (void)awakeFromNib {
self.btn.target = self;
self.btn.action = #selector(popUpActivity:);
[self.btn bind:#"selectedValue"
toObject:self
withKeyPath:#"mySelectedString"
options:nil];
// I've added a couple of additional bindings here; they're
// not required, but I thought they'd be instructive.
[self.btn bind:#"content"
toObject:self
withKeyPath:#"myItems"
options:nil];
[self.btn bind:#"selectedIndex"
toObject:self
withKeyPath:#"mySelectedIndex"
options:nil];
// Now that you've set the bindings up, use them!
self.myItems = #[#"Snow", #"Falling", #"On", #"Cedars"];
self.mySelectedIndex = #3; // "Cedars" will be selected on startup
// no need to set value of mySelectedString, because it will be
// updated automatically by the selectedIndex binding.
NSLog("%#", self.mySelectedString) // -> "Cedars"
}
- (void)popUpActivity:(id)sender {
NSLog(#"value of <selectedIndex> -> %#", self.mySelectedIndex);
NSLog(#"value of <selectedString> -> %#", self.mySelectedString);
}
A final point worth making is that none of the above should be a part of an NSPopUpButton subclass. It looks like you can - and therefore should - do everything you need to do without a custom subclass of this control. In my demo-app the code above belongs to the ViewController class, you should try doing this also.

Pass UILabel through a function

I wonder how can I pass a UIlabel to a function ?
I wrote a function that gives a UILabel text from a table, but now i want to run the function with the UIlabel passed to the head of the function. Just like you would do with the text or int. Do I need a pointer or can I pass the label the normal way?
-(void)giveText:(NSString) *textinsert
{
label1.text=textinsert;
}
How could I save all the labels inside an array and run through the array?
To be honest after reading comments that you have put I don't believe you understanding what's going on at all.
This -(void)giveText:(NSString) *textinsert will just not work syntax error this method should be -(void)giveText:(NSString *)textinsert, but that isn't your problem.
From what I understand about what you are asking you want to pass a UILabel to the method.
So what you want to be doing is is something like
- (void)someMethod
{
UILabel *label1 = [[UILabel alloc] init];
[self giveText:label1];
}
- (void)giveText:(UILabel *)label
{
// Do what ever with label.
}
So what is going on?
We have created the method giveText: that takes a UILabel as a parameter then we call this method from someMethod by doing [self giveText:label1]; we pass in the UILabel that we have created.
Try this
NSMutableArray *arr=[[NSMutableArray alloc]init];
//label Creation
UILabel *lbl=[[UILabel alloc]init];
//add into array
[arr addObject:lbl];
// get label From array one by one
for (UILabel *lbl in arr) {
//pass label to function
[self passLabel:lbl];
}
-(void)passLabel:(UILabel *)lbl
{
// do here
}
you can pass label the same way as for example nsstring.
I think you shouldn't pass label to function if you are simply want to change ot's text.
Just create function which returns string
label.text = [self getLabelText];
- (NSString *) getLabelText {
NSString *text = #"text";
return text;
}

Using NSString value to refer to UILabel

Have mercy, newbie here.
I have a NSString value that I construct on the fly, which is the name of a UILabel instance. I want to send the label a message to update its text. But, the two data types don't match. Here's enough code (I think):
In header file:
IBOutlet UILabel *Clue1; // IBOutlet and IBAction are IDE flags
IBOutlet UILabel *Clue2; // IB = interface builder
IBOutlet UILabel *Clue3;
In implementation file:
- (IBAction) newPuzzle:(id)sender { // Clear all fields & get new clue
[Clue1 setText:#""]; // Clear the fields
[Clue2 setText:#""];
[Clue3 setText:#""];
// Send up a randomly chosen new clue
NSArray *clues = [NSArray arrayWithObjects:#"222", #"333", nil];
NSInteger randomIndex = arc4random()%[clues count];
NSString *aClue = [clues objectAtIndex:randomIndex];
// The clue will be split into component digits and each piece sent to a different label
for (NSInteger charIdx = 0; charIdx < aClue.length; charIdx++) {
NSString *cluePos = [NSString stringWithFormat:#"Clue%d", charIdx + 1];
NSLog(#"%#", cluePos); // works
[cluePos setText:#"test"]; // Xcode notes the type mismatch
}
}
There are some similar questions on SO, but none are close enough for me to recognize that they apply to my case, at least as far as I can tell. Using the terminology of another language (R), I need to "coerce" the class of cluePos from NSString to UILabel. I'm on Xcode 4.2.1 and OSX 10.7.2.
TIA.
You can't coerce a string into a label because they are fundamentally different. The string doesn't have any knowledge of your view controller class or it's properties (some of which happen to be labels).
You can however use the valueForKey: method to get a property of an object by name, where the name is specified as a string. So to get a property called Clue1 on my view controller I'd say:
UILabel *label = [self valueForKey:#"Clue1"];
Or in your case, this:
NSString *cluePos = [NSString stringWithFormat:#"Clue%d", charIdx + 1];
UILabel *label = [self valueForKey:cluePos];
label.text = #"test";
(I'm assuming 'self' in this case refers to the view controller, but you can call this on any object that has properties.)
Another way to do this is to turn your string into a selector using NSSelectorFromString. That would look like this:
SEL selector = NSSelectorFromString(#"Clue1");
UILabel *label = [self performSelector:selector];
For your purposes either solution works equally well, however the advantage of using the selector is that you can pass arguments to the method call (so you could call a method that returns an object, not just access a property or IBOutlet).
Note that both of these methods will raise an exception if you try to access a property or call a method that doesn't exist. You can test if the property exists before calling it by saying:
SEL selector = NSSelectorFromString(#"Clue1");
BOOL labelExists = [self respondsToSelector:selector];
if (labelExists)
{
UILabel *label = [self performSelector:selector];
label.text = #"test";
}
else
{
//do something else
}

Pass Button object to method

I am trying to write a generic method that would allow me to pass the following: "x", "y" "object" and then have it move. Currently I have this:
-(void) changeObjectLocations: (integer_t) xSpot: (integer_t) ySpot: (id) sender {
if (![sender isKindOfClass:[UIButton class]])
{
UIButton *myObject = (UIButton *)sender;
[UIView animateWithDuration:1.5
animations:^{
CGRect newFrame = myObject.frame;
newFrame.origin.x = xSpot;
newFrame.origin.y = ySpot;
myObject.frame = newFrame;
}];
}
else if (![sender isKindOfClass:[UILabel class]])
{
UILabel *myObject = (UILabel *)sender;
[UIView animateWithDuration:1.5 animations:^{
CGRect newFrame = myObject.frame;
newFrame.origin.x = xSpot;
newFrame.origin.y = ySpot;
myObject.frame = newFrame;
}];
}
}
I then want to call it like so:
-(void) orientationBlockLandscape {
[self changeObjectLocations: 456 :282 : btn1] ;
[self changeObjectLocations: 391 :227 : lblTitle] ;
}
Although it is working, on compile I get the following warning:
SecondViewController.m:33: warning: 'SecondViewController' may not respond to '-changeObjectLocations:::'
Is there a better way I can/should be passing the object? Thanks in advance for any and all help.
Geo...
Based on the warning outputted, it sounds like you didn't define changeObjectLocations in the header of your SecondViewController -- or it's not the same signature as what you've implemented.
The compiler looks for selectors which are the method definitions with the variables and return removed.
So a method like:
-(void) setObject:(id) anObject forKey:(NSString *) keyname;
... would have a selector of:
setObject:forKey:
Therefore, your method of:
-(void) changeObjectLocations: (integer_t) xSpot: (integer_t) ySpot: (id) sender
... will have a selector of:
changeObjectLocations:xSpot:ySpot:
Note that the parameter names are part of the selector so:
changeObjectLocations:xSpot:ySpot:
and
changeObjectLocations:::
.. are two entirely separate selectors which represent two entirely separate methods.
Although it is legal in the language to use parameters without names e.g. ":::" it is very, very poor practice largely because it is to easy to get a naming collision. Being explicit not only makes the code easier to read and maintain but makes it easier for the complier and runtime to function.

How to passing a NSMutableArray to another ViewController class

I have created NSMutale Array in "HeroListViewController". I want use it in another viewController which is MapTutorialViewController. I tried like this.
in HeroListViewController.h
MapTutorialViewController *maptutorialcontroller;
NSMutableArray *listData;
set properties and synthesize them correctly
in HeroListViewController.m
- (void)viewDidLoad {
[super viewDidLoad];
listData = [[NSMutableArray alloc] init];
}
- (UITableViewCell *)tableView:(UITableView *)theTableView cellForRowAtIndexPath:(NSIndexPath *)indexPath {
static NSString *HeroTableViewCell = #"HeroTableViewCell";
UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:HeroTableViewCell];
if (cell == nil) {
cell = [[[UITableViewCell alloc] initWithStyle:UITableViewCellStyleSubtitle reuseIdentifier:HeroTableViewCell] autorelease];
}
NSManagedObject *oneHero = [self.fetchedResultsController objectAtIndexPath:indexPath];
NSInteger tab = [tabBar.items indexOfObject:tabBar.selectedItem];
switch (tab) {
case kByName:
cell.textLabel.text = [oneHero valueForKey:#"name"];
cell.detailTextLabel.text = [oneHero valueForKey:#"secretIdentity"];
break;
case kBySecretIdentity:
cell.detailTextLabel.text = [oneHero valueForKey:#"name"];
cell.textLabel.text = [oneHero valueForKey:#"secretIdentity"];
default:
break;
}
[listData addObject: [oneHero valueForKey:#"secretIdentity"]];
count=[listData count];
printf("No of items of listData:%u\n", count);
if(maptutorialcontroller==nil){
maptutorialcontroller= [[MapTutorialViewController alloc]initWithNibName:#"MapTutorialViewController" bundle:nil];
maptutorialcontroller.secondarray=listData;
}
count=[maptutorialcontroller.secondarray count];
printf("No of items of seconarray :%u\n", count);
return cell;
}
OUTPUTS : No of items of listData:3
No of items of seconarray :3 // both are correct
BUT the the problem I have, when I try to use the secondarray in "MapTutorialViewController" like this,
in MapTutorialViewController.h
HeroListViewController *heroviewcontroller;
NSMutableArray *secondarray;
set properties and synthesize them correctly
in MapTutorialViewController.m
- (void)viewDidLoad
{
heroviewcontroller = [[HeroListViewController alloc]initWithNibName:#"HeroListViewController" bundle:nil];
self.secondarray=[heroviewcontroller.listData mutableCopy];
//secondarray= heroviewcontroller.listData;
int count;
count = [secondarray count];
//
printf("No of items of secondarray from MapTutorialViewContriller :%u\n", count);
}
OUTPUT : No of items of secondarray from MapTutorialViewContriller :0
Why it is 0
whats the wrong with my code, please help me
Example
firstviewcontroller .h file
before #interface
use #class secondViewcontroller;
declare this inside of #interface with
secondViewcontroller *sVC;
then in firstViewController.m file
before #implementation
#import "secondViewcontroller.h"
then
-------------------
secondVC.h file
#interface inside declare this
say NSMutableArray *secondarray;
and sythasize them.
-------------------
after this
in firstViewcontroller.h viewdidload create this sVC by alloc and initwithnibname then
sVC.secondArray=urfirstArray;
now while u push this sVC controller to navigation controller u can nslog this array in viewdidload.
This would only work if you create and fill the mutable array in the init method.
You should look into delegation and/or notification.
How is that array being created within HeroListViewController? In this method, you are creating a NEW instance of HeroListViewController and trying to get a property from it. If you already have a HeroListViewController in memory, this is completely wrong.
Make a property on the class for this viewDidLoad method. It should be of type NSMutableArray. When you allocate and initialize this class, call [set myArray:heroListArray] on it from HeroListViewController. That should give you access to it.
I'm assuming that you have a view containing this new view and the hero list view. If that is the case, then you could create a property in the new view like so:
#property (nonatomic,retain)HeroListViewController *heroListViewController;
and then set it equal to the heroList from the outside:
newView.heroListViewController = HeroListViewController;
The main problem with your code at the moment is that you're creating a new instance of HeroListViewController by using alloc init, and you're not accessing the same thing. By setting the new view's heroListViewController property, you can get access to the correct viewController.
Finally, in viewDidLoad of the new view - I'd actually put the code in viewWillAppear:(BOOL)Animated - you can put code to match the arrays.
Note that this whole way of doing it is messy and could be better done with a singleton class if you need access to an array in multiple places. The above will help you get it working quick, but if you want a really clean fix, go here: http://www.iphonedevsdk.com/forum/iphone-sdk-tutorials/24135-singleton-classes.html