Two States -> Two Actions -> One UIButton for iOS 7 - cocoa-touch

I want to accomplish something that It's even hard for me to explain to you, but I'm going yo try to do.
I have one UIButton (Button 1) and want it do to two different actions depending on the context or state of this particular button, but the context or state is controlled by another UIButton (Button 2).
I hope this image can explain what I'm looking for:
Now... i'm not looking for the exact code I need to implement, but an idea of how to accomplish this, maybe some hint about some methods or classes to use.
Note: I already think about to change the UIButton tag to control the action, but I can't change the tag because I need it as it is to control the behavior of actions. Also, (Button1) must return to it's original state once the second action it's used.

From what you've described, I suggest you should have two IBActions and set a property of UIButton 1. Your .h header file will look something like -
-(IBAction)button1Pressed:(id)sender;
-(IBAction)button2Pressed:(id)sender;
#property (strong, nonatomic) IBOutlet UIButton* button1;
Where the button1 property is hooked up to your button one via ctrl-drag in the interface builder.
Then within your .m implementation file, the following set-up will enable you to do what you're looking for.
-(IBAction)button2Pressed:(id)sender{
if (self.button1.selected){
self.button1.selected = NO;
}else
self.button1.selected = YES;
}
So when you press button2, this will change the state of button1 by making it 'selected' or not, which means button1 will do what ever you need, where the state is controlled by button2.
-(IBAction)button1Pressed:(id)sender{
if (self.button1.selected){
//Place your code here for button 1 to do something in this state
} else
//Place your code here for button 1 to do something in this UN-selected state
}
I hope this helps with what you're trying to do.
Thanks, Jim.

You can use the UIButton's UIControlState, with if statements.
(i.e
buttton.selected....
buttton.enabled.....
buttton.highlighted....
)
if I understand correctly?

Related

Click textfield and button disappears (it doesn't, but i want it to)

I have a textfield and a button. When I click inside the textfield, I want the button to disappear. I defined the textfield as both outlet and action ( with event “Did end on exit”). In the method for the textfield, I have self.testButton.hidden = YES; When I click inside the textfield, the button does not go away. Instead, it remains until I hit the return key on the keyboard – causing the keyboard to go away. I tried the same thing w/ touchup inside as the event on the text field. When you click in the text field, nothing happens to the button.
Instead of using the Target-Action mechanism ("Did end on exit" and "Touch Up Inside") use the Delegate mechanism.
First, make your class conform to the UITextFieldDelegate protocol. In your *.h (header) file add the following:
// Here I'm assuming your class is inheriting from UIViewcontroller but it
// may be inheriting from some other class. The really important part here
// is: <UITextFieldDelegate>. That's how you make your class conform to that protocol
#interface THE_NAME_OF_YOUR_CLASS : UIViewController <UITextFieldDelegate>
.
Second, implement the -(void)textFieldDidBeginEditing:(UITextField *)textField method. Also, remember to set yourself as the delegate too: self.textField.delegate = self. That way, the method will get called every time the user starts editing. Inside that methdod call self.testButton.hidden = YES;. In your *.m (implementation) file add the following:
-(void)viewDidLoad {
// here I'm assuming you have a 'strong' reference to your text field.
// You're going to need one to set yourself as the delegate.
self.textField.delegate = self;
}
// This is one of the methods defined in the UITextFieldDelegate protocol
-(void)textFieldDidBeginEditing:(UITextField *)textField {
self.testButton.hidden = YES;
}
.
Similarly, to make your button appear again, implement the - (void)textFieldDidEndEditing:(UITextField *)textField method. Inside it un-hide your button. Again, in your *.m file add the following:
// This is another method defined in the UITextFieldDelegate protocol
-(void)textFieldDidEndEditing:(UITextField *)textField {
self.testButton.hidden = NO;
}
Although delegates may be a mystery to you right now once you become familiar with them
you will realize they're very easy. And this is very important because iOS programming
relies heavily on delegates.
A delegate is a "notification" mechanism based on the "Hollywood" principle which is: don't call us; we'll call you.
In your case the class that contains the UITextField is interested in knowing when the UITextField begins editing and when it ends editing. But your class cannot be "polling" (that is, constantly asking) the text field to find out if the state changed. Instead you register your class with the text field and it will be the text field the one that will let you know when something happened. That will be thanks to the methods that you implemented.
Further reading: protocols and delegates
Hope this helps!
Have you made sure that testButton has its IBOutlet set before you hide it?
If you want to button to disappear when the user begins editing the text field, try UIControlEventEditingDidBegin.

Click on NSTextField Label to emulate hyperlink to a folder

I have an NSTextField label and I want something to happen when a user clicks on it.
I thought I could create an IBAction and link this to the label but nothing seems to happen.
Any suggestions?
Intentions:
The reason why I am doing this is because I want a label that is a hyperlink to a folder. Perhaps I am taking the wrong approach altogether?
IBAction definition in my PersonController.m
- (IBAction)surnameLabelSelected:(id)sender {
NSLog(#"This should do something!");
}
XIB File
In the XIB file I have made a Received Actions connection between surnameLabelSelected and the StaticText NSTextField label.
You've got a couple options. Francis's answer is one option. Another option is to subclass NSTextField and override -mouseDown:. Something like this (written off the top of my head, not tested):
#interface ClickableTextField : NSTextField
#end
#implementation ClickableTextField
- (void)mouseDown:(NSEvent *)theEvent
{
[self sendAction:[self action] to:[self target]];
}
#end
If NSTextField is closer in style to the appearance you need, this might be the better approach. If you need NSButton's features (highlight upon click, etc) go with Francis's solution.
Labels are non-editable text fields thus don't send actions to their targets. You want to use an NSButton and turn off its border-drawing and size it to fit its text as best as possible to simulate a label.
Another option is to create a transparent button (without title/image/border, momentary push in) in front of the label, with the same frame as label's. And set button's action accordingly.

Toggle-style button : how to toggle title in this case?

I have a CoreData app presenting data with a TableView, textfields, buttons... It deals with people situations and one of the button is a toggle-style button with title "Close". When we consider the user's case closed, we press and it changes the state of a boolean-type attribute in the entity, representing the closed/open state of the case, using a simple binding to the attribute value. The button title also becomes "Reopen" as the case may be reopened in the future.
Then additional things had to be done with the data on pressing the button, so I had to create an IBAction method instead of simply use the former binding. Problem: when button is pressed, the action is done, but the button title is not toggled. It makes sense since nothing tells it to toggle anymore.
I decided to remove the action on the boolean from the IBAction and use again the value binding, so the boolean change is performed by the binding and the other operations are performed by the IBAction. Problem: it modifies the data unexpectedly, sometimes working fine, sometimes not doing all things in a coherent way as expected.
So I'm back with all changes handled by the IBAction and this time, I'm using the Title/Alternate title bindings instead of the value binding. Now the button title toggles, but instead of displaying the word "Close" and "Reopen", it displays the boolean values "0" and "1".
I should perhaps handle the button title change in the IBAction as well, using "setTitle", but then I see a new problem coming. On app start-up, how will it pick the appropriate entity record for reference? And what if the table is in a "No Selection" situation? Looks like a quite extensive piece of code to handle such a small issue...
Any advice on a short, more direct way of handling this is welcome. Thanks.
Sounds like you probably have a couple of different options.
This first option is a bit more involved than the others, but is still good to know for the record. This option basically would not use bindings for the close/re-open button, but instead, set the title, etc. programmatically. The basic game plan would be as follows:
The close/re-open button's initial title when the document opens is set in the nib file (e.g. Close).
Optionally, when the document opens, you could disable the button in -awakeFromNib, since the table view will have no initial selection.
The only actions that would necessitate the button's title or enabled state to be changed is 1) when the tableview's selection is changed, and 2) when you've clicked the close/re-open button to toggle the case's state.
To achieve the desired result, you'd create an IBOutlet to the close/re-open button if you don't already have one. You'd then connect up the tableview's delegate outlet to your controller class, which will let your controller class know when the tableview's selection has been changed (via the - (void)tableViewSelectionDidChange:(NSNotification *)notification <NSTableViewDelegate> protocol method). You would also need to update the close/reopen IBAction method to make sure it switches the title on the button after being clicked (since the tableview selection wouldn't change during that operation). The controller class's code might look something like this:
// add this declaration to avoid compiler warnings:
#interface LHController (LHPrivate)
- (void)updateCloseReopenButtonTitle;
#end
- (void)awakeFromNib {
[self updateCloseReopenButtonTitle];
}
- (void)tableViewSelectionDidChange:(NSNotification *)notification {
[self updateCloseReopenButtonTitle];
}
- (IBAction)toggleCloseReopenButton:(id)sender {
// do your existing code here and then add:
[self updateCloseReopenButtonTitle];
}
- (void)updateCloseReopenButtonTitle {
// assuming 'casesController' is an IBOutlet to your NSArrayController
NSArray *selectedObjects = [casesController selectedObjects];
// loop through the selected cases to determine whether
// they're all closed, all open, or mixed to set
// the title of the button appropriately
BOOL allClosed = YES;
for (LHCase *case in selectedCases) {
if ([case isOpen]) {
allClosed = NO;
break;
}
}
[closeReopenButton setTitle:(allClosed ? #"Reopen" : #"Close")];
[closeReopenButton setEnabled:[selectedObjects count] > 0];
}
Before bindings came along, this is how we used to have to do things.
Another option might be reconsidering your user interface: maybe rather than a push/toggle button whose title you need to toggle, you could instead just have a checkbox titled Closed, which would signify whether the selected cases were closed or not. You could use bindings for the checkbox's state, like shown in the image below:
You could then have an IBAction method that handles the extra stuff that needs processing. You can ask the casesController for the -selectedObjects and then loop through them. As long as you make sure the checkbox Allows Mixed states, it has the added advantage of better representing mixed-case scenarios, in case of a selection of cases with mixed open/closed states (the checkbox a dash instead of a full check).
Another option if you want to stick with a toggle button is to create and specify a custom NSValueTransformer for the title and alternate title bindings. This value transformer would take in the boolean closed/open state of the case and turn it into a string more fitting than just 0 or 1 (which is what's being displayed now). It might look something like this:
+ (Class)transformedValueClass {
return [NSString class];
}
+ (BOOL)allowsReverseTransformation {
return NO;
}
- (id)transformedValue:(id)value {
BOOL isClosed;
if (value == nil) return nil;
// Attempt to get a reasonable value from the
// value object.
if ([value respondsToSelector: #selector(boolValue)]) {
isClosed = [value boolValue];
} else {
[NSException raise: NSInternalInconsistencyException
format: #"Value (%#) does not respond to -boolValue.",
[value class]];
}
return (isClosed ? #"Reopen" : #"Close");
}

XCode/Objective-C, load buttons (and other UI elements) at runtime

Is it possibile to load buttons at runtime without using .xib files and connect the buttons to class properties anyway (without drag&drop)?
With the "load without .xib" statement do you mean "instantiate in the code", right? in such case the answer is "yes, of course":
// interface
#property (retain) UIButton *myButtonProperty;
// implementation
UIButton *tmpButton = [UIButton buttonWithType:...];
...
self.myButtonProperty = tmpButton;
Yes, it is. You can create button object and add it to a view. More details can be found here.

How to raise an event in Objective-C

I'm a little confused about raising an event in Objective-C,
I came from C# (.NET) environment and I would like to learn Objective-c and Cocoa programming.
So here's my question:
I have a little application with a NSTextField that I want to use to listen to an event.
What I want to do : When I double click within this control, it raise an event and pop up ex: an NSAlert that displays "Double clicked".
So how can I do that, I'am a visual person so I need some code exemple to show how it's work; Like what should I put within the .h class and within the .m class.
Thanks in advance,
Alex.
You need to read up on the Cocoa Fundamentals and the target/action mechanism. An NSControl (like its NSButton subclass) has a target to which it sends an action with itself as the sender. Not all controls support -doubleAction, but some do.
NSButton/NSButtonCell does not support a double action, so you would need to do some subclassing and override the mouse methods. NSEvent (which is passed into mouse methods) can be queried for its click count to distinguish double-clicks from singles.
Just for the record, it's usually click-and-hold that produces a context menu on OS X and this capability is announced through a down-facing arrow somewhere on the right of the button face. Few people will actually know a menu is there for double-clicking and it's hard to represent this with a symbol on the button face. Consider a click-and-hold trigger for your button's context menu.
I am not sure why you want to do this in a text field, but here is how to do it:
You need to use a subclass that overrides click behavior for double clicks. The header file would look like this:
#import <Cocoa/Cocoa.h>
#interface ClassName : NSTextField {
//Any new instance variables here
}
//Any new methods here
#end
and the implementation file would look like this:
#import "ClassName.h"
#implementation ClassName
- (void)mouseUp:(NSEvent *)event {
//You can also do this with mouseDown:, depending on when you prefer to handle the event
if([event clickCount] == 2) {
//Handle double click here
} else [super mouseUp:event]; //or pass to parent implementation
}