Click events getting routed to the wrong view - macos

I have a window with two views in it:
Both the movie view and the controller view override mouseDown: and mouseUp:, for different purposes (the movie view has a target and action and reacts as a button, whereas the controller seeks and supports dragging).
The twist is, when I click within the movie view—which, as shown in the screenshot, will generally be large enough to make missing it and hitting the controller view instead wildly improbable—the mouseDown: and mouseUp: messages are sent to the controller view! The movie view never receives either message.
I have not overridden hitTest: in the movie view or in its parent view (which is a plain NSView), only in the controller view (for reasons that have to do with a tracking area I have on that view—my implementation simply returns self, which is the controller view).
So, what gives?

Well, you probably guessed it: It was my hitTest: implementation in the controller view.
It turns out that hitTest: is not only sent to the views whose frames the mouse location lies within; it is sent to every view in the window, even views that were nowhere near the mouse at either end of the click.
So, when overriding hitTest:, make sure that you verify that the point is actually within yourself. The simplest way is to send [super hitTest:thePoint] and only do your custom hit-test if the result is not nil. (Unless you want to steal clicks from other views in the window.)

Related

NSWindow display and setViewsNeedDisplay

In NSWindow class I found, among others, two methods: display and setViewsNeedDisplay:. But I don't know the difference between these two methods. Although documentation says: "You rarely need to invoke this method", I need to call one of these to update window's contentView. The problem that the I don't know which method to call. Maybe somebody can tell me difference between those 2 methods?
Thanks.
P.S.contentView of window is my custom view.
For both windows and views, display method redraws the object immediately, and setViewsNeedDisplay:/setNeedsDisplay: set a flag that redraw is needed while the actual redraw will happen later. Repeatedly displaying a view is expensive, repeatedly marking it for display is very cheap.
Most of the time you need to call setNeedsDisplay: on the view you want to be redrawn. Or even setNeedsDisplayInRect: to mark only a part of it, not the whole view. So if all you need is contentView to be redrawn, call [[window contentView] setNeedsDisplay:YES] and that will be it.
In rare cases, for example, before invoking a blocking API call or displaying a modal alert, you will have to call display on the view instead, otherwise the call will block for a long time before redraw happens.
In even more rare cases, you will have to call display on the window, for example, if you tinkered with areas outside content view, like title and borders.
And you almost never need -[NSWindow setViewsNeedDisplay:]. I don't know an example when one needs it.

Issues when reloading views in an NSBox

I have a problem when reloading views. OSX is not as simple as IOS.
In MainMenu.xib I have an NSBox. As per Hillegrath, several views are stored in an array and when a segmented button is pressed the views are exposed. This works properly. My frustration involves revisiting these views when changes have been made that should cause them to redisplay new values through [self someAction]. Actions in two of the views alter (global)values that should propagate changes in the other views. I am using a window controller w/xib (MainMenu) to hold the box which contains the views. I also title the views in code.
The global ivar values change properly when required and the log shows that. However dependent operations do not occur when the view are revisited, ie, update view specifics.
What appears to happen is that loadView is not called when the various views are displayed, ** **. awakeFromNib and loadView operate correctly when each view is first displayed but not ever on redisplays. This implies that that the view may be hidden but viewDidUnhide has no effect.
The view changing code (from an SO MVC answer and Hillegrath) is
NSViewController *activeVC =
(NSViewController *) self.viewControllers[index];
// [_box setContentView:nil];
[_box setContentView:activeVC.view];
[_box setNeedsDisplay:YES];
From the copied code it can be seen that I have also attempted setting the active content to nil before setting a new view but, to no avail.
Any ideas, recommendations, notifications fail to work either but may not set up correctly.
Thanks
It looks like you’re confusing view loading with display. -awakeFromNib is received by a nib file object only when the nib file is loaded. Similarly, -loadView is received by the view controller only when its corresponding nib file is loaded. Since you are keeping your view controllers in an array, those view controllers do not get deallocated, hence their corresponding nib files are never unloaded whilst the array is alive. This is reasonable behaviour, but you must bear in mind that -awakeFromNib and -loadView are only executed once in this case because nib loading is executed only once.
Since it seems that you are manually populating the view in -loadView, you’ll also have to do that whenever you set that view as the box’s content view (assuming you’re not always updating the view whenever the model changes). For instance, you could have a -reloadData method in your view controller and both -loadView and your box’s content view swapping method would use -reloadData.
Alternatively, you could set your model object as the represented object of your view controller and bind the controls in the corresponding view to properties of that represented object. NSViewController exposes a representedObject property that’s convenient for bindings.
For the record, -[NSBox setContentView:] marks the box for redisplay, so you don’t need to send it -setNeedsDisplay:YES.
(and make sure _box actually points to the NSBox instance)

how to get the Bar Button Item from Split View Controller without rotating the device?

I am developering in iOS 5.1 and is pretty happy with these methods in UISplitViewControllerDelegate
splitViewController:willHideViewController:withBarButtonItem:forPopoverController
splitViewController:willShowViewController:invalidatingBarButtonItem:
however, now I notice that these two methods will be only called when the device is rotating and thus the orientation changes. What I am trying to do is to segue(replace segue) my detail view controller to another one. In the new detail view controller, I will always hide the master view controller even in landscape and therefore I need the button.
Without the delegate being called of these two methods, how can I get the button?
If you are hiding your master view controller even in landscape (by means of split view controller delegate), the splitViewController:willHideViewController:withBarButtonItem:forPopoverController: will be called, so you can safely put your code there.

How to make a view controller first responder for an NSView in Cocoa

I'm trying to implement a view controller for a custom NSOpenGLView based view (this is Cocoa, not Cocoa Touch).
The view is contained within a NIB loaded window but it does not have its own NIB. In fact the window contains multiple instances of the view.
I want to route mouse events to the controller instead of to the view. I would like for this to happen as soon as the user clicks within the corresponding view.
So how can this be done ?
I've tried having the view's becomeFirstResponder method call makeFirstResponder with the controller as argument. However that doesn't seem to work, the view still receives the mouse events instead of the controller if NSView::becomeFirstResponder returns YES. If it returns NO then neither of my classes receive the mouse events.
Of course I could implement the mouse event handling methods in the view and explicitly forward them to the controller but it seems like there should be a better way to handle this.
For general "first responder" status, I recommend Charles Parnot's MTViewController, an NSViewController subclass that uses KVO to make certain the controller is in the responder chain with no extra effort on your part.
However, in your case, you want mouse events too. There's really no way around this - your view will need to translate mouse events into controller interactions.

NSTableView not refreshing

I have a table view that gets refreshed two different ways. Both are through a button, and as a matter of fact, both are through the same IBAction in the same class!
Here's my problem:
The buttons are in two different .xib files, the button in the same xib as the table view works perfectly, while the one in the different xib does the method to get the new data, but it DOES NOT refresh the table. Same exact method, different results. To get the IBAction for the other button I simply dragged out an NSObject in IB and set its class to the class of my table view, which contains the IBAction, then hooked it up to my button.
How can I fix this?
Sounds like you're creating a second, parallel, object of your class in the second XIB. The button sends a message to that instance, which does some of the stuff you expect because it's an object of the right class, but it isn't actually the right object and isn't connected to your view.
What you need to do is ensure that both buttons talk to the same instance. This is easiest if the target is in the responder chain -- you should be able to set the button's target to First Responder and the message will find its way to the right place. Otherwise, you need to get a pointer to the target into the XIB, eg as an IBOutlet in the object that will be File's Owner.

Resources