NSTableView keyDown: and mouseDown: - objective-c

I've been working on a menubar note-taking application for Mac. It is written in Objective-C and Cocoa, and I'm using BWToolkit with it. My problem is getting keyDown: and mouseDown: events in a BWTransparentTableView which is a subclass of NSTableView. I just can't get it to work. I've tried searching the Internet, and some places say that you must subclass NSTableView. I've tried that, but it still doesn't work. I am pretty new to Objective-C and Cocoa, and I may just be doing something incorrectly.

Items in an NSTableView will automatically begin editing when they are slow double-clicked or when the Return key is pressed. Make sure that the table view, the cell and the array controller (if used) are marked as editable.
If you are not using an NSArrayController, make sure that your table view has a delegate and that it responds to tableView:shouldEditTableColumn:row:.
To handle a double click, you just need to set the doubleAction of the table view:
- (void)awakeFromNib
{
[tableView setTarget:self];
[tableView setDoubleAction:#selector(doubleClickInTable:)];
}
- (void)doubleClickInTable:(id)sender
{
NSInteger rowIndex = [sender selectedRow]; //Use selectedRowIndexes if you're supporting multiple selection
//Handle the double click
}
Note that neither of these methods require you to subclass NSTableView.

Related

get indexPath for customCell in TableView

I have a UITextField in a customCell. I want to be able to get the text that user inputs in it. In my customCell.m file I can get the text that is inputed with
[self.aTextField addTarget:self action:#selector(aTextFieldEditing:) forControlEvents:UIControlEventEditingChanged];
but I cannot get the indexPath so I can save it to the NSMutableArray that holds all the UITextFields.
In my TableView I cannot get the didSelectRowAtIndexPath to run. I also cannot get the textFieldDidBeginEditing method to output anything. If it helps I have a TableView with sections.
My idea at the moment is to get the indexpath.row and indexpath.section from the tableView and save them as an extern so I can access it in the customCell.m
I would be grateful for any ideas or specific examples of how I could do this. Or a different cleaner way to accomplish what I want.
You should be able to get the index path using the -indexPathForRowAtPoint: method on UITableView.
You can do something like this (not tested):
- (void)aTextFieldEditing:(UITextField *)textField {
CGPoint convertedPoint = [textField.superview convertPoint:textField.center toView:self.tableView];
NSIndexPath *indexPath = [self.tableView indexPathForRowAtPoint:convertedPoint];
// Use your index path...
}
Edit:
In the case your cell is receiving the edit events, you can always define a protocol and make your view controller a delegate of the cell. You could then call this delegate method from your aTextFieldEditing method and pass the cell. Now that you have the cell you can call -indexPathForCell on your table view. If none of this makes sense, look into the delegate pattern. It's very common in Cocoa/Cocoa Touch and is very well documented online.

Xcode - setFocus on a text field, becomeFirstResponder isn't enough

At the moment, I trigger a method on 'Did End On Exit' in my app (I'm aware that this may not be the greatest way of doing it but I'm very new to Objective C and Xcode for that matter and I'm simply doing what feels comfortable to me).
This method resigns the firstResponder from the current text field and applies it to a later text field.
The problem I'm facing is that the keyboard covers the next text field so that the use has no idea where the focus is and therefore what they are required to type.
How do I get it so that my keyboard shifts down and actually shows the text box that is currently active? Making something the firstResponder simply doesn't do what I want it to, unless there's part of the implementation I'm missing.
Here's my simple method:
- (IBAction)firstNameNext:(id)sender {
[firstNameTextField resignFirstResponder];
[surnameTextField becomeFirstResponder];
}
Any advice would be super.
Add UIScrollView in your main view then all contents as subview to UIScrollView
Now when specific UITextField needs to be able to visible in view use its delegate like this:
Note: add UITextFieldDelegate in .h file like this
#interface yourViewController : UIViewController<UITextFieldDelegate>
Also bind with File's Owner
- (BOOL)textFieldShouldBeginEditing:(UITextField *)textField;
{
if(textField == yourSpecficTextField) //one u want move upwards
{
yourScrollView.contentOffset = CGPointMake(0,200); //required offset
}
... //provide contentOffSet those who needed
return YES;
}
- (void)textFieldDidEndEditing:(UITextField *)textField
{
yourScrollView.contentOffset = CGPointMake(0,0); //make UIScrollView as it was before
}
If you have keyboard input fields that will be covered by the virtual keyboard, then you need to move those fields out from under the virtual keyboard.
The normal way to do this is to have the controller's view be a scrollable view like UIScrollView. Moving Content That Is Located Under the Keyboard gives a very robust way of adjusting your scroll view and ensuring the required field shows.

Passing NSIndexPath to New View

I am having real troubles passing an NSIndexPath to my new view. This is how the app works:
I have a UIBarButtonItem in my nab br, tap that and you get a popover view, this shows a bunch of stuff. I need to get an NSIndexPath from my main view, to this popover view.
I have a property for the NSIndexPath in my popover view class and the popover transition is connected up in my storyboard.
Then I have this code to pass the index path across views:
-(void)prepareForSegue:(UIStoryboardSegue *)segue sender:(id)sender
{
if ([segue.identifier isEqualToString:#"statsPopover"])
{
StatsViewController *statsVC = [segue destinationViewController];
statsVC.selectedIndex = stageSelectionTable.indexPathForSelectedRow;
}
}
However, while this gets called, the index path isn't actually sent between views. My index path on the popover is always the default, 0,0 row and section.
You say you know from debugging/logging that the method is running, your if statement is triggered, and stageSelectionTable.indexPathForSelectedRow has the value you expect.
Doing that kind of diagnosis puts you on the right path to solving your issue. Keep at it -- if you test some other things with NSLog or the debugger you should be able to find the problem.
Do you know that statsVC is non-nil? If it's nil, your message to set selectedIndex does nothing.
Is statsVC the class you expect? If, say, the (table?) view in your popover is embedded in a navigation controller, segue.destinationViewController will point to the nav controller, and you'll need to look into its child view controllers array to find the one you're looking for.
Is whatever accessor method your StatsViewController class is using for its selectedIndex property working right? (This shouldn't be a problem if it's a synthesized accessor for a strong/retain property.)
In your StatsViewController class, are you trying to work with selectedIndex before it actually gets set? Setting a property in prepareForSegue:sender: and then using it in viewDidLoad should work fine, but using it in awakeFromNib might not, and using it in an init... method definitely won't.

How to get a reference to the view controller of a superview?

Is there a way to get a reference to the view controller of my superview?
There were several instances that I needed this on the past couple of months, but didn't know how to do it. I mean, if I have a custom button on a custom cell, and I wish to get a reference of the table view controller that controls the cell I`m currently in, is there a code snippet for that? Or is it something that I should just solve it by using better design patterns?
Thanks!
Your button should preferably not know about its superviews view controller.
However, if your button really needs to message objects that it shouldn't know the details about, you can use delegation to send the messages you want to the buttons delegate.
Create a MyButtonDelegate protocol and define the methods that everyone that conforms to that protocol need to implement (the callback). You can have optional methods as well.
Then add a property on the button #property (weak) id<MyButtonDelegate> so that any class of any kind can be set as the delegate as long as it conforms to your protocol.
Now the view controller can implement the MyButtonDelegate protocol and set itself as the delegate. The parts of the code that require knowledge about the view controller should be implemented in the delegate method (or methods).
The view can now send the protocol messages to its delegate (without knowing who or what it is) and the delegate can to the appropriate thing for that button. This way the same button could be reused because it doesn't depend on where it is used.
When I asked this question I was thinking of, in a situation where I have custom cells with buttons on them, how can the TableViewController know which cell's button was tapped.
More recently, reading the book "iOS Recipes", I got the solution:
-(IBAction)cellButtonTapped:(id)sender
{
NSLog(#"%s", __FUNCTION__);
UIButton *button = sender;
//Convert the tapped point to the tableView coordinate system
CGPoint correctedPoint = [button convertPoint:button.bounds.origin toView:self.tableView];
//Get the cell at that point
NSIndexPath *indexPath = [self.tableView indexPathForRowAtPoint:correctedPoint];
NSLog(#"Button tapped in row %d", indexPath.row);
}
Another solution, a bit more fragile (though simpler) would be:
- (IBAction)cellButtonTapped:(id)sender
{
// Go get the enclosing cell manually
UITableViewCell *parentCell = [[sender superview] superview];
NSIndexPath *pathForButton = [self.tableView indexPathForCell:parentCell];
}
And the most reusable one would be to add this method to a category of UITableView
- (NSIndexPath *)prp_indexPathForRowContainingView:(UIView *)view
{
CGPoint correctedPoint = [view convertPoint:view.bounds.origin toView:self];
return [self indexPathForRowAtPoint:correctedPoint];
}
And then, on your UITableViewController class, just use this:
- (IBAction)cellButtonTapped:(id)sender
{
NSIndexPath *pathForButton = [self.tableView indexPathForRowContainingView:sender];
}
If you know which class is the superview of your view controller, you can just iterate through the subviews array and typecheck for your superclass.
eg.
UIView *view;
for(tempView in self.subviews) {
if([tempView isKindOfClass:[SuperViewController class] ])
{
// you got the reference, do waht you want
}
}

NSView's context NSMenu is never shown even though all the right methods are being called

I have an NSCollectionView with a bunch of NSViews in it, stacked vertically, to make it look a bit like UIKit's UITableView. Everything works as expected, except for one thing:
When right-clicking any one of the NSViews, I expect the NSMenu I set to be view's menu to be shown, but alas - nothing happens.
The crazy part is all the right methods are being called, exactly as could be expected: -rightMouseDown:, -menuForEvent: and finally -menu.
When I set up any object as the NSMenu's delegate, menuWillOpen: is not called, so it seems to me something fails over on Apple's side of things, just in between asking for the menu, and actually showing it.
Would anyone be able to shed a light on this?
Thanks in advance.
PS. For what it's worth, NSMenus I present manually (without relying on Apple's right-click handling) using popUpMenuPositioningItem:atLocation:inView: are shown.
Edit / Update / Clarification
The NSCollectionView in question is inside an NSWindow that's being shown when an NSStatusItem is clicked, like CoverSutra/TicToc/what have you. Some code from the MyWindow NSWindow subclass:
- (void)awakeFromNib {
[self setStyleMask:NSBorderlessWindowMask];
[self setExcludedFromWindowsMenu:YES];
}
- (BOOL)canBecomeMainWindow {
return YES;
}
- (BOOL)canBecomeKeyWindow {
return YES;
}
- (BOOL)isMovable {
return NO;
}
- (void)presentFromPoint:(NSPoint)point {
point.y -= self.frame.size.height;
point.x -= self.frame.size.width / 2;
[self setFrameOrigin:point];
[self makeMainWindow];
[self makeKeyAndOrderFront:self];
}
presentFromPoint: is the method I use to present it from any point I like, in my case from just below the NSStatusItem. (Not really relevant to this problem)
My application has LSUIElement in its Info.plist set to YES by the way, so it doesn't show a menu bar or a Dock icon. It lives in the status bar, and has a window that's shown when the NSStatusItem is clicked.
The view hierarchy is as follows:
MyWindow => contentView => NSScrollView => NSCollectionView
The NSCollectionView has an NSCollectionViewItem subclass connected to its itemPrototype property, and the NSCollectionViewItem subclass has an NSView subclass connected to its view property.
The NSView subclass, in turn, has an NSMenu connected to its menu property.
And last but not least: This NSMenu has one NSMenuItem sitting inside it.
Both the NSCollectionViewItem subclass and the NSView subclass do nothing interesting as of now, they're just empty subclasses.
The NSMenu connected to the NSView's menu property is what should be shown when the NSView is right-clicked, but as I hope I have made clear: It isn't actually shown.
Update
I still have no idea what caused this problem, but I've decided to 'move on' from NSCollectionView, as it wasn't really fit for what I was trying to do anyway, and I am now using TDListView which works like a charm.