I have a class MyImage, that have a NSAffineTransform member transform.
In my AppDelegate a have a NSMutableArray of images.
I have a MyView that draw the images from AppDelegate.images. So it must track changes in AppDelegate.images and redraw the content.
What is the best way to implement this though bindings?
You don't mean bindings, or if you do you have misunderstood how bindings work and what they are for.
For an explanation read this post - Manual binding in Cocoa
You need to use KVO to observe the array of images. You need to make sure you add and remove images from the array in a KVC compliant way.
Related
What would be the best way to implement (using Cocoa) a DataView as presented in this example:
1. IKImageBrowserView
The image below is taken directly from Apple's Image Kit documentation and does exactly what you need.
2. NSCollectionView
NSCollectionView is more general because you are not restricted to just presenting images; you can present a collection of any NSView subclass you like. Try this if you need more customisation. For example, you could have a NSImageView, NSTextField and NSButton in your subclass if you needed to have a more complex user interface.
Is there a simple way to add a simple rectangle to a Custom View without using a custom NSView subclass for it? Something along the lines of:
Assign an IBOutlet (let's call it colorWheelView) of NSView type to the CustomView
In my NSViewController's initWithNibName use it to change draw the rectangle:
// pseudocode
self.colorWheelView.addRectangle(myRectangle);
self.redraw()
The only way I've seen it done (on this site, and in my book Cocoa Programming for Mac OSX, pp. 241) is by making a custom class for the Custom View and modifying its drawRect method... Is this really the only way to accomplish this?
Edit: not sure why formatting is not being rendered correctly. I'm trying to fix it.
It really isn't all that hard to roll your own..
Just add an NSArray property to your NSView subclass, then in your drawRect method draw them either manually or using one of the NSRectFillList* methods provided by AppKit already.
(Beware: those take a plain C array, not an NSArray).
You wouldn't want to manually trigger the redraw from outside the view as in your sample code, though. To keep things consistent your addRectangle would trigger a redraw of the view itself e.g. by calling setNeedsDisplay:.
I need to create/use a NSTableView programmatically. From the documentation, it seems that I would implement the NSTableViewDataSource protocol. But the function tableView:objectValueForTableColumn:row: suggest (because of the row index) that I would have to manually take care of the sorting. Is that right? Also, as this is called on every redisplay, that might be slow because I am using Python and it would mean a Python call for every row/column.
I wonder wether it make sense to use Cocoa binding and wether that would be more simple. In any case, I would have to do that programmatically and I am a bit stumbled about how to that. From other examples, I guess I would create a NSArrayController and bind it all together somehow.
Also, I want to have it working on older MacOSX, so I guess I have to use the cell-based NSTableView, whatever that means.
The data source will be static and is not editable, i.e. I can just provide a NSArray with the data.
There are three ways to use NSTableViews: 1) delegate methods; 2) NSArrayController; or 3) Bindings. My best advice to you is to learn all three of these in Xcode on a Cocoa ObjC project first before attempting to do this in python. Note: I'd also recommend that you first learn how to do these via nibs and then figure out how to do it programmatically (again in Xcode on a Cococa ObjC project before attempting it in python).
If you understand how Interface Builder (view in Xcode 4, app pre-Xcode4) bindings work then for the following code "Bind To" corresponds to myController, "Controller Key" would be "selection", and the Model Key Path would be "fullPath".
[myView bind: #"valuePath"
toObject: myController
withKeyPath: #"selection.fullPath"
options: nil];
You just need to sort your array once, then when the delegate method is called access the appropriate index in the array.
You really should have a good read of the Table View Programming Guide.
In learning Core Animation, I learned very quickly that if you don't do it right, you get really weird undefined behavior. To that end, I have a few questions that will help me conceptually understand it better.
My NSView subclass declares the following in it's init. This view is a subview of normal layer backed view.
[self setLayer:[CALayer layer]];
[self setWantsLayer:NO];
After this, when and in what situations should I refer to self as opposed to [self layer]? I have been ONLY manipulating the layer with explicit and implicit animations, staying away from [self setFrame:] etc. and using [[self layer] setPosition] etc.
The problem with this approach is that the actual frame of the view stays in one spot throughout any and all animations applied. What if my view is supposed to recieve mouse events? For example, I have a view that uses core animation and it is dragged around by the mouse. Is there a way I can somehow keep the view frame synced with the current state of the presentation layer so I can receive proper mouse events?
About the presentation layer, apparently it's only available when an actual animation is in progress. Is there any sort of property of the layer that can tell me where it's ACTUALLY visually at even when an animation's not in progress?
I think you need to re-phrase your question a little. It seems there is some underlying misunderstanding, but you're not really expressing it very clearly. You're question title suggests you're looking to understand something more theoretical, but your actual question suggests you're looking for something more concrete. Let me see if I can clarify a few things.
The presentationLayer provides information about the layer's current state while "in-flight".
When there is no animation occurring, the presentationLayer and the layer information will be identical. Query the layer's bounds, frame, or position to find out where it is currently in its parents coordinate space.
NSViews must have layer backing enabled to be able to perform animations.
Make sure you're not just animating with an explicit animation and not actually setting the layer value that you're animating. Animations don't automatically change the properties of the layers they're animating. You have to change the property to the ending value yourself or it will just "snap back" to the starting value.
If you want to animate the view, as opposed to a layer, you can use the animator proxy, like [[view animator] setFrame:newFrame];
Wrap calls to the animator in a CATrasaction to alter things like animation duration.
Let me know if you need some clarification by updating your question. Providing some pertinent code would really help identify the problems you're having trouble solving.
Firstly, you want to use [self setWantsLayer: YES]. Also, it's only important to call -setLayer: before -setWantsLayer: if you want to provide a specific CALayer subclass (such as a CAScrollLayer); if you just want a regular CALayer you just call -setWantsLayer: and it'll be created for you. Even better, just check the 'wants layer' option in Interface Builder.
Secondly, the entire point of using a layer-backed view is that you can continue to use the regular NSView methods and get the free CoreAnimation 'tweening' effects. If you want to use CoreAnimation as your only means of moving items around, then the correct way to do so is to create a layer backed view which contains your pure-CALayer presentation hierarchy.
I've not looked at any freely-available CoreAnimation tutorials, but I can definitely recommend the Pragmatic Programmers' book on the subject. They also have a screencast available by the book's author.
Will the individual UML diagram shapes be NSView subclasses or NSBezierPaths? How are the diagrams created and managed?
One way to do this is to:
Create a document-based app
Design model classes for the different objects the end-user will be able to draw in your canvas, all sharing one abstract superclass
In your CanvasView class, implement drawRect and have it call the NSDocument subclass, or for more granular classes it's viewcontroller, to get all the objects that should be drawn in the right order to draw them.
For each of these objects, call a drawInteriorInView:rect: method or something similar that they all have implemented, from within your CanvasView's drawRect: implementation.
The advantage of such a granular design is that you can decide to replace NSBezierPath drawing with straight CoreGraphics calls if you find a need to do so, without having to completely re-architect the app.
Typical Cocoa controls, like for instance a tableView, implement a bunch of different drawing methods, one for the background, one for the gridlines, etc. etc. all of them called (when applicable) from the view's drawRect:.
Or you could of course look at GCDrawKit, which seems to have a pretty functional implementation. Especially check out the sample app that comes with it.
Have you looked at OmniGraffle? It may do what you need.
[non-programming-related answer...]
Have you looked at the Sketch example project, found in /Developer/Examples/AppKit? It should get you at least halfway to where you're going.
You would typically start with an NSView subclass to represent your "canvas" and handle drawing and mouse/keyboard events. You would probably use NSBezierPath inside your drawing methods to fill and outline the shapes. Depending on how complex the drawing code is, you might put everything in your NSView subclass, or make an NSCell subclass that would take some work out of the NSView. In either case you would want to define a data source protocol (or create bindings) to provide data to the NSView from the objects in your data model which represent UML items.
Core Animation would be worth considering too, although I would start with NSView at the beginning, at least for a simple prototype.