Implementing context menu for NSTableCellView - objective-c

I have a table view where I would like to have a different context menu appear when right-clicking on a cell vs. when right-clicking on an area where there are no cells. I'm trying to do this using storyboards in the interface builder.
If I attach a menu to the NSTableView I can get a context menu to appear. However, if I attach a menu to the NSTableCellView I get the NSTableView's context menu rather than the menu I have attached to the NSTableCellView.
I know I could do all this via code by having one menu attached the the NSTableView and change it's contents based on the cell clicked but I believe I should be able to do this by attaching different menus to different views in the view heirachy.
Can this be done solely in interface builder?

As per Willeke's suggestion I sub-classed the NSTableView and added the the following code to the menu property:
- (NSMenu*) menu
{
if (self.clickedRow == -1 || self.clickedColumn == -1) return super.menu;
NSTableCellView *clickedCell = [self viewAtColumn:self.clickedColumn row:self.clickedRow makeIfNecessary:FALSE];
if (clickedCell != nil) return clickedCell.menu;
return self.menu;
}
With this implementation I get the behavior I was looking for.

Related

Disable menu highlight when pressing shortcut key

I would like to disable the Application "menu highlight" that happens when you press a shortcut key assigned to an NSMenuItem that belongs to the specific menu in question.
The issue is that in the application you use the keyboard quite a bit and having the menus becoming highlighted all the time becomes a bit annoying but I still want to have the menus (including the shortcuts) there as it shows the user which actions that can be used.
Declare a custom NSMenuItem subclass and start using that custom class instead of NSMenuItem.
In this class you should override this method:
- (BOOL)isHighlighted
{
return NO;
}
This way you will not have the menu item highlighted.
EDIT
Try this:
[item setOnStateImage: item.offStateImage];
FFR: Look up the following methods in the docs:
- (BOOL)validateMenuItem:(NSMenuItem *)menuItem
Will work for both selecting the menu item and the associated command key.
Within your NSDocument provide a body for validateMenuItem
such as,
- (BOOL)validateMenuItem:(NSMenuItem *)menuItem
{
SEL theAction = [menuItem action];
if (theAction == #selector(openPreferencesPanel:)) {
return !_isCurrentlyModal; //A BOOL in MyDocument
}
return [super validateMenuItem:menuItem]; // Keep this for proper cut, paste, etc validation
}
In your case, the above selector might be highlight:. Check the nib/xib and inspect it. It might be attached to the First Responder. Copy the method name.
Also have a gander at for more general items (buttons, etc) and also includes menu items.
- (BOOL)validateUserInterfaceItem:(id <NSValidatedUserInterfaceItem>)anItem

Get the index of view from which a button was clicked in a NSCollectionView

I have a NSCollectionView and I am adding my custom view which acts as NSCollectionViewItem for that collection view. In my collectionViewItem, I have a NSButton along with various other elements. I have a method onButtonClick which is connected to that button. Now, suppose I add 5 items of my collectonViewItem on to the collectionView.
How can I get the index of the view from where the button was clicked?
Inside onButtonClick, I tried following code but it always returns 0 regardless of which button I click:
id collectionViewItem = [sender superView];
NSInteger index = [[colloectionView subviews] indexOfObject:collectionViewItem];
What is the right way to achieve this?
you cant return index by clicking a control (which having its own functon).i think you have to see http://andrehoffmann.wordpress.com/2009/08/29/nscollectionview-tutorial-for-dummies-xcode-3-1-3/.
before that check collectionview selection as selectable in attributes inspector.(and also try by disable the button)

Is there a way to select a segue anchor programmatically?

Suppose I have a Storyboard containing a view that contains a button. When the user presses this button, a popover comes up.
Thus, I need to set an anchor by dragging the segue to the button using Xcode (and then do performSegueWithIdentifier:).
So, my question is: is there a way to set this "anchor" programmatically?
Thank you.
In my case I've added programmatically several UIBarButtonItem.
The problem of only using an invisible view as an archor is that, if like in my case, the size of the UIBarButtonItem is changing it's size, the arrow of the popover doesnt appear centered, and althought it works, looks a bit strange.
How to solve it.
Create a small view in storyboard ( the size doesnt really matter ), make it invisible, and link it.
In my case this is called invisibleViewAsArchor
Connect the UIBarbutton item with the follow action.
-(IBAction) showMyPopover:(id)sender {
if([self.popoverController isPopoverVisible])
{
[self.popoverController dismissPopoverAnimated:YES];
}else{
self.invisibleViewAsArchor.frame = CGRectMake([sender view].frame.origin.x,
[sender view].frame.origin.y-50,
[sender view].frame.size.width,
[sender view].frame.size.height);
[self performSegueWithIdentifier:#"segue_to_something" sender:self];
}
}
as you can see before it shows the popover (with performSegueWithIdentifier), I'm changing the frame
of the Archor with the values from the button that has fired the event.
Hope it helps.
In the storyboard anchor the popover to some arbitrary button. Don't worry too much about which one as it will get overridden in the code.
In the view controller method prepareForSegue, add the code:
let dest = segue.destinationViewController
dest.popoverPresentationController?.barButtonItem = <your bar button here>
or if you want to anchor to a view instead
dest.popoverPresentationController?.barButtonItem = nil
dest.popoverPresentationController?.sourceView = <your view here>
You can't programmatically create segue's as explained here: Creating a segue programmatically, however, you can configure which destination controller you want to display at run-time. This is explained in the apple documentation here: Configuring the Destination Controller When a Segue is Triggered.
Hopefully this helps!
I had the same problem where I was creating a BarButtonItem programmatically. You may also be able to get around it by creating an invisible, disabled button which you can set as the anchor in IB.

Add a sub-view to a DetailView UIView in iPad

I'm creating a split view controller app, the detail view has a segmented control in a navigation bar at the top. Clicking on a segment will add a new view to the detail view with the appropriate information on it (covering up the DetailViewController's default UIView).
I've created two new UIViews, corresponding to each segment, and I'm trying to add them to the view like this (in DetailViewController.m):
if (exerciseSegmentControl.selectedSegmentIndex == UISegmentedControlNoSegment) {
NSLog(#"No segment selected");
}
UIView *viewToShow;
if (selectedView == 0 && exerciseSegmentControl.selectedSegmentIndex == 1) {
viewToShow = exerciseSolutionView;
}
else {
viewToShow = exerciseView;
}
[self.view addSubview:viewToShow];
I see the view appear, but it's in the wrong place, it is placed at the very top of the window, instead of below the navigation bar.
In IB, I've created instances of the views, and I've used the Attributes inspector to specify "Navigation Bar" for top bar, which sets the height of the view correctly. But the view is clearly being added too far up in the window - I see the view below it (the DetailViewController's UIView) peaking out at the bottom (I changed the background color so I know which view I'm seeing).
Any tips on how to get the subview I'm adding to get placed correctly in the window?
Thanks!
probably you might want to add Navigation Bar in IB (or in codes) manually.
setting Navigation Bar in the top bar settings doesn't really add a Navigation bar nor reserve some space for it.
anyway.. what i normally do is to add 2 views in IB, then stack them properly.
then link them wif IBOutlet.
That will most probably solve your problem

Getting NSArrayController item for right click in NSCollectionView

I'm trying to create a file explorer using nscollectionview and am currently implementing a right click menu for each item (i.e. copy/delete/rename/etc). I currently have:
An NSCollectionView linked with an NSArrayController which holds a custom object
A subclass of NSBox as the view for each item, this also tracks mouse events and passes them to the controller
The controller has an NSMenu outlet (rcMenu) and also an NSView outlet (itemView) for the NSBox subclass that should be where the menu popup
The code for calling the menu is:
[NSMenu popUpContextMenu:rcMenu withEvent:event forView:itemView];
Once run, this works in that the menu pops up when right clicking the item in the collection view, but on inspecting the event that's passed to the controller, there's not really anything I could use to find out which item was right clicked other than the x,y coordinates (which seem to be for the NSWindow rather than the item or NSCollectionView). What I really want is the object in the NSArrayController that had it's view right clicked.
Is this down to me setting it up incorrectly, is there an easy way to figure it out, or is it just that tough to work it out?
You might try setting the menu of each collection view item's view. Most likely, you'll do this by overriding +defaultMenu in your item view class. Once you do that, comment out the popUpContextMenu:withEvent:forView: message and see whether you can get away without it.
Furthermore, it would then not be too hard to serve up different menus for different kinds of items (e.g., folders vs. packages vs. files, and different types of files at that). You'd probably have to override -menuForEvent: instead of +defaultMenu.
I found an other solution that might help.
For this solution I made a subclass of NSCollectionViewItem and NSView, respectively (and for the ease of explaining) ItemViewController and ItemView.
I'm assuming you work with IB where you have already bound your NSCollectionView to the ContentArray of your NSArrayController (also bind the selectionIndexes).
Next add an ViewController object to the NIB and make sure its custom class is set to the ItemViewController. Now connect it to the itemPrototype outlet of your NSCollectionView.
Next add a Custom View object to the NIB and set its custom class to ItemView. Connect its outlet to the view property of your ItemViewController.
In the interface file of ItemView create a representedObject-like property. With this I mean something like:
#property (nonatomic, assign) id someRepresentedObjectPropertyName
This will be the property which will represent the item in your NSArrayController.
Now go to the implementation file of ItemViewController and override the -setRepresentedObject: method. In here we will first let the ItemViewController handle setting its representedObject, afterwards we assign the same representedObject to the property we made in ItemView. The override would look like:
-(void)setRepresentedObject:(id)representedObject {
[super setRepresentedObject:representedObject];
//Do some appropiate checking on the representedObject...
if (self.view != nil) {
[(ItemView *)self.view setSomeRepresentedObjectPropertyName:self.representedObject];
}
}
Now if you go back to the implementation of ItemView you can override the method -rightMouseUp: and build/set-up a NSMenu there and use the -popUpMenuPositioning...: method. The someRepresentedObjectPropertyName property of ItemView should be set to the correct item in your NSArrayController.
EDIT:
Instead of overriding -setRepresentedObject you could also bind the ItemView's someRepresentedObjectPropertyName to representedObject.someRepresentedObjectPropertyName