I've created a custom subclass of NSCell with an NSImageCell, some NSTextFieldCells, and an NSPopUpButtonCell.
I'm initializing the pop up cell using:
myPopUpCell = [[NSPopUpButtonCell alloc] init];
[myPopUpCell setBordered:NO];
[myPopUpCell setAutoenablesItems:NO];
[myPopUpCell addItemsWithTitles:[NSArray arrayWithObjects:#"Item1", #"Item2", #"Item3"]];
And drawing it in drawInteriorWithFrame:inView:
Everything seems to work great, except that when clicking on the pop up cell while running my app the cell does not pop up. Any suggestions about what might be wrong?
Drawing the pop-up button cell in drawInteriorWithFrame:inView: is going to do just that; draw it, but nothing else. Handling click events is unrelated to drawing, so you're going to have to do some work in your custom cell to interpret mouse events, and if they're inside the frame you're using for the pop-up button, pass them on to the button cell. Start by subclassing the methods listed in the NSCell docs under tracking the mouse, such as –trackMouse:inRect:ofView:untilMouseUp:, and you should be be able to figure out what's needed to make the button cell act correctly.
Depending on what you're doing you may actually find it easier to draw the title string yourself, and just use NSMenu's +popUpContextMenu:withEvent:forView:.
Related
I have a simple app with a Tool Bar at the bottom. Depending upon the context of what the user is doing, I may want various buttons visible or not in the Tool Bar. So, in my code, I remove the weak modifier for the IBOutlet reference to those buttons, and I remove the non-visible buttons in viewDidLoad. Great.
Now, when the context changes, I add/remove buttons as desired using
toolBar.items?.removeAtIndex(0)
toolBar.items?.insert(playButton, atIndex: 0)
Great, works well. But for aesthetics, I would like to have the button removal/insert animated. Apparently the animated:YES argument from Objective-C days is gone. Do you know, does the API support animation of the button shuffling? Or, if not, how best to accomplish this?
Thanks!
I'm working with a view-based NSTableView that uses a custom cell view with several controls in it. One of the controls is an image-only NSButton that either shows a checkmark image or no image at all. Additionally, when I mouse over one of these buttons that has no image, I would like a "faded" version of the checkmark image to be displayed while the mouse is inside the button.
I've gotten the code set up using NSTrackingArea to respond to -mouseEntered: and -mouseExited: events, calling -setImage: on the NSButton to show the faded checkmark while the mouse is inside the button, then set the image back to nil when the mouse exits.
As far as I can tell, all that code seems to be working as expected, but while the button will update and show the checkmark image the first time the mouse enters the view, then disappear when it exits, moving the mouse back over the button a second time does not cause the button to redraw for some reason, leaving it always blank after the first redraw no matter how many times the image gets set.
This is a summary of the relevant code:
- (void)refreshActiveButton
{
self.activeButton.image = self.activeImage;
}
- (void)mouseEntered:(NSEvent *)theEvent
{
NSLog(#"mouse entered %#", self.listItem.name);
self.mouseOverActiveButton = YES;
[self refreshActiveButton];
}
- (void)mouseExited:(NSEvent *)theEvent
{
NSLog(#"mouse exited %#", self.listItem.name);
self.mouseOverActiveButton = NO;
[self refreshActiveButton];
}
- (NSImage*)activeImage
{
NSLog(#"returning new active image");
if (self.listItem.isActive)
return [NSImage imageNamed:#"checkmark16"];
else if (self.mouseOverActiveButton)
return fadedCheckmark;
else
return nil;
}
The button is set up in a .xib file as a "Momentary Push In" (I've also tried "Momentary Change" - same behavior) with no title displayed and no border.
Running in the debugger, I've been able to confirm that:
The -mouseEntered: and -mouseExited: methods do continue to get called when moving the mouse over the button, even when the display doesn't update.
The correct image is being assigned to the button, and is returned back from the button instance after being set. (either the fadedCheckmark image when the mouse is inside, or nil when the mouse is outside)
I tried calling -setNeedsDisplay:YES on the button after assigning the image, as well as setting a layerContentRedrawPolicy of NSViewLayerContentsRedrawOnSetNeedsDisplay (the entire table view is layer-backed) with no change in behavior. Edit: also tried disabling layer backing altogether, with no change.
I feel like there must be something obvious staring me in the face that I'm missing, but if anyone can clue me in, I'd appreciate it.
The short answer: it's a bug! I've filed it with Apple as radar 20774731, and on Open Radar at http://openradar.appspot.com/radar?id=4957762287042560
The basic sequence is, if you set the button's image to something, then set it back to nil, all subsequent setImage: calls have no effect on the button, forever as far as I can tell. I did forget to mention in the original post that this is all on 10.10.3. I did find some interesting stuff when debugging though.
It appears that once you set an image on the NSButton, after an additional pass of the run loop, it gains an NSImageView as a subview, which is presumably doing the actual drawing of the image. Apple has said they are moving NSCell towards deprecation, so I guess this is the first step, with relieving NSButtonCell of the responsibility of actually drawing the image.
So, the first time you set the image, the image gets set correctly on both the NSButton and the NSImageView. When the image gets set back to nil, they both get their images set back to nil as well. However, when you set the image a second time, while the NSButton's own image property does return back the new image, the underlying NSImageView's image property remains nil. So that might explain why it's not drawing anything.
Ultimately what I ended up doing was using a plain NSImageView with a transparent NSButton right on top of it. I don't get the highlighting you get when you click on a button, but I can live with that.
I'm placing a few buttons in a simple rectangular NSview which acts as a custom toolbar. On first render the buttons/views come out as expected, but every time a button is pressed (and sometimes with no mouse interaction at all) artefacts start appearing.
Before
After
I can eliminate the artefacts by calling a [self.toolbarView setNeedsDisplay:YES] in all the action and focus methods but this seems like a hack, is there any clean way to deal with this?
It was a beginner's problem. In the drawRect method
- (void)drawRect:(NSRect)dirtyRect
I was using the param dirtyRect for drawing an outline of my view, assuming it was the view's bounds, where in fact it was only the area around the buttons that became dirty when they were pressed. The 'artefacts' were actually my outline being drawn in the wrong place.
By correctly using the bounds of the view
NSRect drawingRect = [self bounds];
the 'artefacts' no longer appeared.
You just try to set focus ring for a buttons to 'none' in IB.
I have an NSTextView in an NSScrollView, and I am programmatically inserting an NSView subclass as a subview of the NSTextView. This NSView acts as an overlay, superimposing graphical information about the text beneath it.
I thought it was working quite well until I noticed that the text view does not respond to right clicks. Other operations (editing, selection) seem to work just fine.
Also, if the first responder is changed to a sibling of the scroll view (an outline view, for example) the text view does not regain first responder status from clicking on it. The selection will change in response to clicking, but the selection highlight is gray instead of blue (indicating that the text view is not the first responder).
If I offset the frame of the overlay subview, the text view behaves 100% normally in the area not overlapped by the overlay, but the overlapped area behaves incorrectly, as outlined above.
Steps To Replicate This Behavior on Mac OS X 10.6.4:
Create a plain old non-document-based Cocoa app.
Add an `NSTextView' IBOutlet to the app delegate .h.
Add an NSTextView to the window in MainMenu.xib. Connect the textView outlet.
Type in a bit of code:
In applicationDidFinishLaunching:
NSView *overlay = [[NSView alloc] initWithFrame:textView.bounds];
[textView addSubview:overlay];
[overlay release];
Run the app, observe that right click in the text area does not work as it should, yet you can still otherwise interact with the text view.
Next, add an NSOutlineView to the window in the xib. Observe that once focus leaves the text area (if you click on the outline view) with the overlay in place, you cannot set the focus back to the text view (it will not become first responder again).
Is there some way I can enable the NSTextView to receive all of its events, even though my NSView overlay does not accept first responder or mouse events? I suspect this might be related to the field editor – perhaps it is ignoring events it thinks are destined to the overlay view?
You probably need to make your overlay an instance of a custom view class that forwards all events and accessibility messages to the text view. You may also need to convert any view-relative coordinates to the text view's coordinate system.
I don't have a lot of experience with it, but another possibility would be to use a Core Animation layer as an overlay.
A clean way to handle this is by making your overlay view a custom subclass of NSView, and then overriding the hitTest: method to always return nil. This will prevent the overlay view from participating in the responder chain. Instead, events will get sent automatically to it's superview or views higher up the view hierarchy. You might also want to override acceptsFirstResponder to return NO to be safe (in case it's accidentally set programatically).
I'm creating a custom subclass of NSPopUpButton and NSPopUpButtonCell to display a lone icon on top of the button instead of the usual text.
To do this I'm overriding
- (void)drawInteriorWithFrame:(NSRect)cellFrame inView:(NSView *)controlView
in my button cell subclass to draw my icon at the appropriate size and location. When the user mouses over the button I then want to change the image. My only problem is I can't figure out how to make the button cell redisplay (by calling drawInterior... again). By default it only seems to redisplay when the button is clicked or when focus is moved to another window. I've tried setNeedsDisplay:YES but that doesn't seem to do it.
The way I found to do this was to insert a call to
[self updateCell:self.cell];
inside my NSPopUpButton subclass immediately after any point I changed the image displayed by the cell.