NSButton Doesn't Respond to Click When Putting inside M3NavigationView - cocoa

I'm using M3NavigationView to do view navigations, but there I got a problem when push one view inside(some animation happens here), A button doesn't responds to mouse click most of the time, I'll have to click it twice,
I clicked once on the button, and it seems the mouse up event doesn't happen. I have to click the button again to trigger my action.
After more testing today, I found the button inside works normally after it is displayed for 2 seconds. more weird.
Is there anyone knows that going on here?

I used one out of two of my apple developer support service, and get it solved. row double clicked of PXListView needs to change a little to work with M3NavigationView
if([theEvent clickCount]>1) {
if([[self delegate] respondsToSelector:#selector(listView:rowDoubleClicked:)]) {
[[self delegate] listView:self rowDoubleClicked:[theCell row]];
return; //return here, this is the change
}
}

Related

-[NSButton setImage:] only redraws the first time it's called

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.

Why is the NSStatusItem displaying multiple times?

A NSStatusItem has a NSMenu attached, and one of the buttons of the NSMenu opens a NSWindow. Whenever one of these buttons is clicked, the window opens as expected and works properly, but another display of the NSStatusItem is opened.
The NSStatusItem is a clock, so I can see that it is updating correctly. However, the cloned NSStatusItem doesn't have its own menu. If I push the button that makes the window more times, more cloned versions of the NSStatusItem pop up.
Everything works fine except for this.
That's not a whole lot of information to go off of, but there's nothing else I can think of that could potentially help you. I would be happy to provide more information or try something.
EDIT: Every time the button is clicked, awakeFromNib is somehow called, which is why another half-working NSStatusItem happens.
EDIT: Temporary workaround is to put the awakeFromNib method in a dispatch_once.
EDIT: Added method that is triggered when button is clicked, as suggested by #zpasternack
- (IBAction)preferences:(id)sender {
self.windowController = [[NSWindowController alloc] initWithWindowNibName:#"PreferencesWindow"];
[[self windowController] showWindow:self];
}
Is the NSStatusItem contained in the PreferencesWindow nib? That might explain it, since you're loading the nib each time the button is clicked.
Also, is there a reason you need to recreate that window each time the button is clicked? Maybe you could only do it the first time?
- (IBAction)preferences:(id)sender {
if( self.windowController == nil ) {
self.windowController = [[NSWindowController alloc] initWithWindowNibName:#"PreferencesWindow"];
}
[[self windowController] showWindow:self];
}

Cocoa ignore clicks but still register them?

Is it possible to have a click-through window in cocoa (as in _window.ignoreMouseEvents = TRUE), but still find out when the mouse has been clicked above the window? Or, instead of ignoring the events, registering them, and then somehow forwarding them, propagating them to whatever is behind the window?
I guess what you are looking for is the NSView method hitTest.
This method is called on every view when a mouse click is received.
- (NSView*)hitTest:(NSPoint)aPoint
{
return self;
}
Returning self would mean that the click is not forwarded to the next subviews.
Returning [super hitTest] would just forward the click to the parent view of your view hierarchy. Using this, you could simply register clicks without anything else happening.
So something like:
- (NSView*)hitTest:(NSPoint)aPoint
{
[self propagateEventToNextApplication];
return [super hitTest:aPoint];
}
Hope this helps!

xcode iphone back button

Seems like this would be a simple thing, but I cannot find any good info on it.
I just want to be able to 'force' the back button to show. I have a main view and when I transfer to another view there is no back button. But then if I transfer to a 3rd view, the back button appears. This seems to always be the case. Only the 2nd transferred to view shows a back button. I need it to show up on all views except the main view.
I dont need to override it, just simply force it to show where it is not showing...
I know this is an old question, but this might help...
...sometimes you need to embed a view inside a Navigation Controller for the back button to show. Simply select your view controller and select Editor -> Embed In -> Navigation Controller.
If the button does not show (usually if you are presenting a modal view rather than push) then you will need to manually make a 'Cancel' button. Create an action for this and in the method put (I call my method switchback):
-(IBAction)switchback:(id)sender {
[self dismissModalViewControllerAnimated:YES];
}
Patch's solution (dismissModalViewController)had been unfortunately deprecated and replaced by dismissViewControllerAnimated.
Here is the current one:
-(IBAction)switchback:(id)sender {
[self dismissViewControllerAnimated:YES completion:nil];
}

NSTableView, NSArrayController and reload only after key press?

I have the following situation:
There is one custom view inside of the first window that contains a NSTableView.
There is a second window which acts as a form for the current object behind the selection of the table view inside the first window.
Some more details:
I’ve implemented the setDoubleAction: behavior in the NSTableView that basically opens the second window
The table view is bound to the arrangedObjects of an (subclassed) NSArrayController
The specific interface elements in the second window (that opens on double click) are bound to the selection of the NSArrayController
I’ve subclassed the NSArrayController and modified the following functions:
At first I changed addObject: (or add:, this doesn’t really matter):
- (void)addObject:(id)object
{
[super addObject:object];
[self saveTemplatesToDisk];
}
Then I changed remove:
- (void)remove:(id)sender
{
[super remove:sender];
[self saveTemplatesToDisk];
}
The action that opens the preference sheet is just a one liner: [NSApp beginSheet:preferenceWindow modalForWindow:[_preferenceView window] modalDelegate:nil didEndSelector:NULL contextInfo:NULL];
The code that get’s executed after the user presses the return key / OK button isn’t complicated either.
It just saves the current content of the array controller to disk and closes the second window:
- (IBAction)endPreferenceSheet:(id)sender
{
[templateArrayController saveTemplatesToDisk];
[NSApp endSheet:preferenceWindow];
[preferenceWindow orderOut:nil];
}
Finally here’s my problem / question
When I press the return key in the second window, the window closes, the data gets saved and the NSTableView gets properly reloaded without any further interaction. But when I press on the OK button with the mouse, nothing seems to happen. Here’s the interesting part: when I now select another row in the table view in the first window after the second window disappeared, the previously selected row (read: the updated object) gets properly reloaded and displays the content I’ve edited in the second window that has interface elements bound to the selection.
Basically my implementation works, but not when the user uses the mouse to close the window.
The only difference I can spot is the currentEvent, but I can’t imagine how this could change the behavior of this simple application.
When I press the OK button with the mouse: NSEvent: type=LMouseUp loc=(563.055,30.1484) time=58450.2 flags=0 win=0x0 winNum=5371 ctxt=0x0 evNum=8093 click=1 buttonNumber=0 pressure=0 subtype=NSTabletPointEventSubtype deviceID=0 x=19469 y=15838 z=0 buttons=0x0 pressure=0.000000 tilt={0.453108, -0.140629} rotation=0.000000 tangentialPressure=0.000000 vendor1-3=(0, 0, 0)
When I press return: NSEvent: type=KeyDown loc=(0,300) time=58474.8 flags=0 win=0x0 winNum=5371 ctxt=0x0 chars="
" unmodchars="
" repeat=0 keyCode=36
Any ideas how I can solve my problem?
Remember the responder chain: The keyboard event starts at the first responder, which will be the field editor, then (if that doesn't handle it) goes to the next responder, which will be the table view. The mouse event goes directly to the view that the user clicked on, which is the button.
So, the difference is that the table view handles the return event, but it never sees the mouse event. When the user clicks, you simply get an action message from the button—the table view remains in in editing mode.
The solution is to have the action method tell the controller to commit editing before proceeding with the actual action.

Resources