Update NSView in function of events - cocoa

I have a main view (subclass of NSView) and as i'm new to cocoa, i'd like to know how to update the view in function of events.
I know there are many methods that take events such as -(void)mouseMoved:(NSEvent*)event or - (void)mouseClicked:(NSEvent*)event My algorithm to determine what to do is ready. I want to know where I should update the main view: is it in the -(void)mouseMoved:(NSEvent*)event or in the - (void)drawRect:(NSRect)dirtyRect. And if it is in drawRect, then how should i pass the information to it?
Thanks in advance!

Here's a quick explanation that will hopefully get you on your way:
Handle events
User actions are communicated to your views and windows by events (keyboard + mouse) and actions (events interpreted by buttons and other controls). Your views should react to these events and actions by updating the model, which are the lower-level data structures that represent whatever your program displays to the user. If Cocoa, the view typically communicates through a controller object to make changes to the model.
Invalidate display / trigger redraw
After you have updated your model, you need to inform the view that it needs to redraw. This can be done in several ways, but the simplest way to do it is -setNeedsDisplay:YES. This will ensure that at some point in the immediate future, your view will redraw itself to display the updated model data.
Draw
At some point Cocoa will call -drawRect: on your view. Inside -drawRect:, you should read the requisite data from your model and draw the necessary graphics. You shouldn't do any manipulation of the model in this method.

Related

View/Screen/Activity navigation in MVP

When using MVP, where should the logic of navigation be stored? Is it in View or is it in the Presenter?
Say View1 (backed by a presenter Presenter1) has a button PushMeToGo somewhere in its view hierarchy. Said button should move the user from View1 to View2 when pressed.
Should the logic of moving into View2 be handled directly inside View1 or should it notify its presenter which will then execute the command instead?
This question was asked here 5 years ago: In MVP, who should handle navigation? . Just let me make a little contribution. A view should be as dummy as possible containing no business logic at all (except of direct user interaction or, let's say, animation). Thus, the View1.PushMeToGo button could simply fire an event to notify the Presenter1 that this button is pressed, and let the presenter delegate the navigation request to whatever that's responsible for navigatigating between views (let the presenter be as clean as possible). The V->P notification mechanism is on your own: an event bus, a simple observer, a single handler notified via Presenter1.onPushMeToGoClicked that's defined in the presenter interface as well. If you unit-test your code, this also lets you test the onPushMeToGoClicked handler having dummy views.

Cocoa OS X Bindings and Non-trivial data model

This project is to create an editor tool in Cocoa & Swift for Mac OS X that will edit a non-trivial data structure. A pared down schema looks like this:
Game
title : String
[ Room ]
Room
roomKey : String
roomName : String
roomDescription : String
[ Object ]
[ Exit ]
Object
objectDescription: String
Exit
destinationRoomKey : String
sourceRoomKey : String
The current implementation - the third go-around - has a single Document.xib file (the app is document based) and in that I'm hooking up a NSObjectController to the base game object loaded by the document, and an NSArrayController to the game objects array of rooms. There's an NSObjectController for the Room. I have not done the objects or exits yet.
The views are handled by a base root view controller, which swaps sub-views in and out as you go up and down the view hierarchy. On the view for the root game state, you click an "edit" button that slides in the table view for the list of rooms. Clicking a button in one of the room rows slides in a room detail view which has its own controller.
This is all working well enough. I have hooked up the object controller of the room so that it gets the selected room of the rooms array as its object, using Interface Builder bindings. I can do this because I have all the views, view controllers and data model controllers in the one XIB file.
However: now I am adding the game objects to this mix and the XIB file is getting very unwieldy. I really feel like I want to do this in separate XIB files, but when I tried that previously I was not able to hook up the controllers to each other. I tried manually writing code to load & save the data at the same time as the controllers had their view displayed and removed but this was flakey and error prone. So far the most elegant and robust result I have had is with this one XIB approach.
I looked at the programmatic API for binding but could not understand how to get it to work, or how to discover what the key path would look like. I suppose if it was possible to do the bindings programatically you could put the different parts in different XIB's and do the bindings at load time. But I could not find any examples of anyone doing that successfully and it seemed a road to madness.
At present I'm having no problems with Swift and its relations to Cocoa and Objective-C so if anyone has answers in Objective-C or Swift I'd be happy to hear them. I have not put Swift as a key word for this question as its not part of the problem.
I've seen the StackOverflow answer about hierarchical models, and its what I'm currently doing, so it doesn't help. The problem is that this approach gets unwieldy when there's several layers of master-detail.
I've also seen the StackOverflow answer about sharing controllers, and it was what I tried before and where I ran into the issue described there, that if you specify a controller object in a NIB it will get instantiated as an independent object. Hence why I have the huge-mega-NIB-of-death approach at present.
I could make the title of this question "cannot make programmatic bindings work" but I'm not sure that that is the right approach anyway.
Surely someone has done the job of making a non-trivial data model work with Cocoa before?
Your secondary NIBs should be view NIBs, their owners would be an instance of NSViewController or a custom subclass. That has a representedObject property. The NIB and its view controller class should be thought of as stand-alone, theoretically-reusable components. That is, in theory, that NIB could be used in multiple contexts to represent a particular kind of object. So, you should typically not want connections to other parts of your UI or their controllers, other than knowing what object this view is being loaded to represent.
Within the NIB, you can either bind to the File's Owner with a model key path that goes through representedObject or add an NSObjectController that binds to File's Owner's representedObject and then bind your views through that with controller key selection.
When you load such a secondary NIB, you would have to set its representedObject to the object it's supposed to represent, taken from the array controller's selection. This should be done in code, presumably the same code that decides it needs to load the NIB and does so.
If the design of your UI is such that a detail view needs to trigger a behavior that's best handled at a higher level — for example, a Room view needs to arrange for an Exit view be slid into the window, but not as a subview of its own view — the detail view controller should define a delegate protocol and implement a delegate property. For example, the Room view controller's delegate protocol might have a method -roomViewDidChangeSelectedExit:. The Room view controller would call that on its delegate, passing self. You would set some coordinating controller (perhaps the window controller) as the the detail view's delegate.
It's not clear to me if the "detail" views and the "master" views are visible simultaneously. That is, can the user change the object that the detail view is meant to show without backing up first? If so, there are a couple of approaches.
You could set up the bindings programmatically when the view is loaded. This would be the responsibility of the controller that loaded the detail view. It's not the responsibility of the detail view's controller. That doesn't have the higher-level perspective and knowledge to set up the binding. Anyway, you could do it like:
[detailViewController bind:#"representedObject" toObject:self.arrayController withKeyPath:#"selectedObjects.firstObject" options:#{ }];
Be sure to call -unbind: before the detail view controller is released.
The other way to do it is to simply observe the changed selection using a non-Bindings approach, and set the new representedObject in the code that gets triggered. For example, if your master view lets the user select an item in a table view, you would set up the table view's delegate (almost certainly already done) and implement -tableViewSelectionDidChange:. In that delegate method, query the newly-selected item and assign it to detailViewController.representedObject.

In MVC Who is Responsible for Animations?

This question is about a Cocoa app I'm working on but it could apply to anything using an MVC or related architecture.
Who is responsible for animations?
I can see two arguments:
1) Animation code should exist in a view (part of a view's presentation, how it draws itself) but be controlled by a controller (interpreting user input, etc).
2) Animations and their lifecycle should be managed completely by a controller and act on the views belonging to that controller.
I think first option is better because it would be fast if we can handle the events at view itself. Animation is most of the cases do not need additional data...so there is no need to reach to controller.
I think an animation is a modification of the model which is shown by the view. That's why I see the animation handling in the controller.

MVC Mouse events in view design question

This is a design question regarding an MVC implementation. I am creating a 2D graphic app using QT and OpenGL but I do not think the technology matters.
So my view is an openGL widget, whatever is to be drawn is stored n the model and the controller should modify the model and have the OpenGL widget redraw the scene.
The view should capture the following mouse events, MouseRelease, MouseDown and MouseMove and then transfer them to the controller to make the proper decision on what to do when the user clicks or drags the mouse.
I am debating between 2 approaches, incapsulate the mouse handling inside the OpenGL widget and just report the click and drag back to the controller?
Or transfer the mouse events as is to the controller and let it handle all the logic to determine the clicks and drags.
Any advise is very apreciated.
Thank you
I think the widget is going to be getting mouse coordinates in the viewport/"view space" coordinate system, which may not make much sense to the controller. I think your widget should convert the co-ordinates of any clicks and drags to world space, then pass them to the controller.
Why is this good? Because it avoids your controller needing any special knowledge of the viewport/widget, and so preserves encapulation. If you add more viewports/widgets or maybe even a console or a script that also wants to feed the controller, they can all pass their "instructions" in world space and the controller will function quite happily. Your viewport is already "aware" of "world space" and "view space" or it couldn't have rendered your model.

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.

Resources