Where does my First Responder Chain start? - objective-c

I am in he final step to design my application.
It Basically consists of two tableviews plus other views. Each TableView has its own TableViewController class instance.
What I would like to do is to activate the corresponding menu items when the user clicks one of the table view row. For example, I want to delete the file the user is currently selecting, so the file menu save is activated. But if he is not in the table view, I want to gray it out in the menu.
Therefore, I want to use First Responder. I added menu item, defined an action delete: in the First Responder and linked the menu item to that action. TableViewController is a subclass of NSViewController. In The TableViewController.m, I write in the init method (to insert the view controller in the responder's chain):
[self setView:_tableView];
[self setNextResponder:self.view];
[self.view.subviews enumerateObjectsUsingBlock:^(NSView *subview, NSUInteger idx, BOOL *stop) { [subview setNextResponder:self]; }];
But My method delete: doesn't get called! When the user click the tableview, it becomes the First Responder right? Then THe ViewController should receive the action.
Otherwise, What is the First Responder and how to set it?
I guess I'm missing something!

reason it doesn't work
the firstResponder is the first view, or the window, or the app itself that responds to the selectors
basically
id firstResponder = keyView;
while(![firstResponder respondsToSelector:yourSelector] && firstResponder != nil) {
firstResponder = firstResponder.superview;
}
if(!firstResponder && [keyView.window respondsToSelector:sel]) {
firstResponder = window;
}
if(!firstResponder && [application respondsToSelector:sel]) {
firstResponder = application;
}
ViewControllers are not in the responder chain and never get the selector
see https://developer.apple.com/library/ios/documentation/EventHandling/Conceptual/EventHandlingiPhoneOS/event_delivery_responder_chain/event_delivery_responder_chain.html
solution
add the VC into the responder chain
id resp = myTableView.nextResponder;
myViewController.nextResponder = resp;
tableView.nextResponder = myViewController;

I found the solution to this problem. I just forgot to add a semicolon to my actions when I defined them in the First Responder.
SO I entered "delete" instead of "delete: " And just for this reason, my method -(void)delete:(id)sender was not called!
Thanks for your help anyway!

Related

How do I properly use NSUserDefaults Class to bypass a View?

I have a project in xcode that uses storyboards. The first view that loads is an "accept terms and conditions" view in which the user must click an accept button to proceed. After clicking it, it segues to the next view. After the user clicks accept the first time the program launches, I never want them to see that view again - I want it to go straight to the next view. I have some code but its not working. Here is what I have exactly:
In app delegate: (inside applicationDidFinishLaunchingWithOptions)
if([[NSUserDefaults standardUserDefaults] boolForKey:#"TermsAccepted"]!=YES)
{
[[NSUserDefaults standardUserDefaults] setBool:NO forKey:#"TermsAccepted"];
}
Inside the accept terms and conditions view implementation: (viewDidLoad)
if ([[NSUserDefaults standardUserDefaults] boolForKey:#"TermsAccepted"]){
[self.navigationController pushViewController: self animated:YES];
//I want it to go to the next screen
}
else {
//I want to show this screen, but I don't know what goes here
}
Also Inside the accept terms and conditions view implementation (in the accept button)
- (IBAction)acceptButton:(id)sender {
[[NSUserDefaults standardUserDefaults] setBool:YES forKey:#"TermsAccepted"];
}
I run it and get the error: 'Pushing the same view controller instance more than once is not supported'. How do I fix this?
In your first code snippet, you basically say "if TermsAccepted is not YES (so it is NO), then set it to NO. This does not make sense
In your 2nd code snippet, you wrote [self.navigationController pushViewController:self animated:YES];. So basically you ask the current UIViewController (self) to push itself on its own navigationController… which does not make sense either.
That's why you have this error. You try to push the current viewController self whereas it is already on screen in your navigationController. So you try to push the same instance (self) twice on the same navigationController.
You obviously meant to push another viewController (probably an instance of a TermsAndConditionViewController or something that shows the terms and conditions of your app) on the navigation controller, and not the current viewController itself, which doesn't make sense.
First, you want to have the next view controller, the one you always want to show, be the root view controller of your window. In that controller's viewDidLoad method, put your if clause to show the accept terms and conditions controller -- you can show that one using presentModalViewController. The if clase can be like this:
If([[NSUserDefaults standardUserDefaults] BoolForKey:#"TermsAccepted"] !=YES) {
// instantiate your terms and conditions controller here
// present the controller
}
Then, in the method where you dismiss the terms and conditions controller, set the value of that key to YES.

How do I deselect all table rows in NSOutlineView when clicking in the empty space of the view?

For example when I click on the red dot below:
I want the following deselection to occur:
I set up the view-based NSOutlineView using bindings for both the data source and the selection indexes. So far i've tried to override the TableCellView becomeFirstResponder and also override NSOutlineView's becomeFirstResponder however it seems NSOutlineView never actually gives up first responder status?
Some advice would be very much appreciated!
I found this post on the topic. The solution appears to be in creating a subclass of NSOutlineView and overriding mouseDown: so that you can determine whether the click was on a row or not. When the click is on a row you just dispatch to super. If it's not you send deselectAll: to your NSOutlineView.
I haven't tried it myself but there are various posts around which come up with comparable code.
Use setAction: method of NSOutlineView.
[mOutlineView setAction:#selector(doClick:)];
[mOutlineView setTarget:self];
-(IBAction) doClick:(id)sender;
{
if ([mOutlineView clickedRow] == -1) {
[mOutlineView deselectAll:nil];
}
}
Swift 5. in NSOutlineViewDelegate
func outlineViewSelectionDidChange(_ notification: Notification) {
//1
guard let outlineView = notification.object as? NSOutlineView else {
return
}
outlineView.deselectAll(nil)
}

How do I cancel a segue that is connected to an UIButton

I'm working on this tutorial, which shows how to create an app with storyboards. My viewController has two UIButtons and I have connected a segue to those UIButtons. These segues push a new viewController.
Now I am looking for a way to cancel this segue if a certain condition becomes true. When I used the old xib files to create my interface I was able to do something like this:
-(IBAction)sendButton:(id)sender {
if(TRUE) {
// STOP !!
}
}
This does not work anymore. How can I cancel the push of the new viewController?
Nope. You can't cancel segues that are directly linked to interface elements like your UIButton.
But there is a workaround.
Remove the segue that is linked to the button
Add a segue from the viewController (the one with the button) to the viewController that should be pushed. This must be a segue that is not connected to a interface element. To do this you can start the segue from the status bar. Or create the segue in the left sidebar.
Select this segue, open the attributes inspector and change the Identifier of the segue (e.g. PushRedViewController)
Select Xcodes assistant view, so you can see the .h file of the viewController with the button.
Connect the button to an action. To do this select the button and control-drag to the .h file. Select action in the menu and name your action (e.g. redButtonPressed:)
Open the implementation of your viewController
change the action from step 5 to something like this:
- (IBAction)redButtonPressed:(id)sender {
[self performSegueWithIdentifier:#"PushRedViewController" sender:sender];
}
You can actually do this by adding following method into your code
- (BOOL)shouldPerformSegueWithIdentifier:(NSString *)identifier sender:(id)sender {
if([identifier isEqualToString:#"btnSegue"])
{
return YES;
}
else{
return NO;
}
}
Lets assume your segue is btnSegue.And if you need the segue to be performed based on some condition you can have following code
if(check)
{
[self performSegueWithIdentifier:#"btnSegue" sender:self];
}
Where check is BOOL which you can set true or false based on your condition.

Change default insertTab: action in NSSearchfield

I have a view with a nssearchfield a nstableview and a nsmatrix with three radiobuttons. Using delegates i change the selected radiobutton when the searchfield is the firstresponder and the user press tab, that works perfectly but what i want is that the searchfield don't loose the firstresponder when the user press tab
You can sub class NSSearchField and add this function
- (BOOL)resignFirstResponder {
return NO;
}
It will refuse to relinquish first responder status.
Another way is catch the windowDidUpdate notification. These are sent whenever anything changes, including change of focus, so you can check for the firstResponder and make it become first responder again.
[searchField becomeFirstResponder];

Why is it that my UITableViewController setEditing:animated: method is not called?

I must be doing stupid, but I can't see what: my UITableViewController subclass is never called when the edit button of my navigation is pressed.
What could be causing that?
My view hierarchy is loaded from a Nib file and put inside a popover. The [+] button is connected to the insertNewObject action of my UITableViewController subclass. It works fine.
The [Edit] button however has no action to connect to. The doc says it will automatically call the setEditing:animated: method of the view controller, which I override.
The nib file is set up pretty much as usual AFAICT. And in fact, I'm not sure what additional detail I can give that would suggest my mistake.
What is the control flow from the click on the [Edit] button to the call of the setEditing:animated method?
I feel like we must be missing the same thing.
Whatever the case, I made it work by doing the following.
IBOutlet UIBarButtonItem *editButton;
-(IBAction)editButtonPressed:(id)sender {
[self setEditing:YES animated:YES];
}
- (void)setEditing:(BOOL)editing animated:(BOOL)animate
{
if(self.tableView.isEditing)
{
self.editButton.style = UIBarButtonItemStylePlain;
self.editButton.title = #"Edit";
}
else
{
//self.editButton.style = UIBarButtonSystemItemDone;
self.editButton.style = UIBarButtonSystemItemEdit;
self.editButton.title = #"Done";
}
// Toggle table view state
[super setEditing:!self.tableView.isEditing animated:animate];
}
I hooked the editButton up to the button I added to the nav bar and it's action to the editButtonPressed IBAction. After doing that my setEditing: is called (obviously) and the super call toggles the table view's editing state.
I'd like to use the system defined button styles, but the appropriate one is commented out because while it did change style I couldn't figure out how to change the text from "Edit" to "Done" so I had to do it all manually (that only worked if I left the button as Custom and set the style generically). This has the downside of not being localized (for free), etc.