Dragging from NSBrowser and use bindings? - cocoa

I've implemented a browser that shares NSTreeController with NSOutlineView so I can easily switch between them and keep the states in sync. Implementing drag support for NSOutlineView is simple, but it appears that NSBrowser cannot support dragging while using bindings. Has anyone been able to support dragging from NSBrowser while also using bindings?

I solved this by creating custom subclasses of NSBrowser, NSMatrix, and NSBrowserCell. I had to force NSBrowser to use NSRadioModeMatrix mode (single cell) by setting this mode in NSBrowser subclass -mouseDown method because it was constantly being reset.
In the NSBrowserCell subclass, I implemented trackMouse:inRect:ofView:untilMouseUp: by simply having superclass do it's thing, but returned NO so tracking would occur, which allowed mouseDragged: to be called in my NSMatrix subclass. In mouseDragged: I then did all the stuff to get the mouse location, construct a mouse image, and used [self dragImage:at:offset:pasteboard:source:slideBack:].
I'm not sure if this is the best approach, but it worked. It only works when there is a single selection allowed in NSBrowser.

Related

Does Interface Builder's new IBDesignable attribute work with NSButton and other controls in Cocoa?

I have managed to get the IBDesignable/IBInspectable attributes working with direct subclasses of NSView but not with a direct subclass of NSButton. This is causing me to question if in fact the Cocoa implementation is somehow limited to NSView only.
Almost every example on the web (and Apple WWDC 2014 Xcode video) use NSView and then drag a custom view component from the library onto the canvas (and then change its class).
Is it possible to use IBDesignable with subclasses of NSControl and NSButton etc...? I have seen many examples on the web using UIButton.
If it is possible, then what are you supposed to drag from the library onto the canvas? It doesn't make sense for it to be a "custom view". On the other hand, there is no "custom control" available.
To be clear, I can get the IBInspectable attribute to show up at design time; but any changes don't seem to live render at design time.
The workaround is to wrap any custom NSButton I want to create within an NSView (via composition) but this seems like a bit of a hack...
I started playing around with a custom NSButton and NSButtonCell.
Dragging a button from the library onto the canvas and changing its class and the cell class doesn't live render. I think this is because Interface Builder still does a lot of custom things to setup NSButtonCell.
What works fine for me is dragging a custom view from the library onto the canvas and set its class. For this to work you need to setup the cell inside NSButtons -initWithCoder:.
Also I found a sample from Apple with a layer-backed custom Checkbox.
You need to drag an NSButton onto the view, then set the Custom Class to your specific NSButton descendant. Not sure why it doesn't work when you start with an NSView.
What can give you a hint is that the NSButton specific attributes aren't in the "Attributes Inspector". Hence there must be some setup at the time you drag the control onto the view.

Animating setHidden: on NSView via Cocoa bindings

I'm currently putting the final touches on a project.
A lot (if not all) of the UI logic currently relies on Cocoa Bindings.
Some of the user interface elements (labels, buttons, etc.) have their "Hidden" bindings defined. When certain events are triggered, these elements visibility is toggled.
I'm trying to animate the visibility change (by animating the opacity and maybe even the scale). This could easily be accomplished in a number of ways, either by setting the relevant layer properties, adding the animations to the layer, etc. However, since I'm trying to totally rely on the bindings behavior I "can't" really do this directly.
I tried an implementation using Layer actions, by defining actions for the keys kCAOnOrderIn and kCAOnOrderOut on the relevant elements, but it really didn't work, as the setHidden: is most likely being triggered on the NSView instead of the CALayer -- which makes sense.
So, my question is: how would you animate setHidden: on a NSView, when setHidden: is being invoked by the Cocoa Bindings.
Thank you.
This will fade out an NSView...
[[someView animator] setAlphaValue:0.0f];
Animating setHidden will have no visual effect since it's either on or off. If you want to animate visibility, use setAlpha (or setOpacity on the layer) instead. These take a value between 0.0 and 1.0. If you need the hidden flag to get set for the sake of state information, call -performSelector:withObject:afterDelay passing it a selector that sets the hidden value to whatever you need it to be after the animation has completed. Alternatively you can set up a delegate for explicit animation to be called back when the animation finishes and call setHidden then.
I would suggest taking a look at NSViewAnimation. It takes any NSView and can animate the frame, size or visibility.

Capturing Window Events in NSDocument

I have an document-based Cocoa application with a TextView and I would like to capture clicks on it, so I'm trying to intercept Window events like mouseDown, mouseUp, etc. then relate them to my TextView.
I've tried a two things:
1.) I made the TextView the initial first responder for the Window of my document, and overrode the mouseDown event on my document class, but it's not hitting.
2.) I subclassed NSWindow and override mouseDown, and set that subclass to my Window's class in my document xib. That event didn't hit either.
I noticed that the Window's delegate is already set to my File's Owner which is my NSDocument subclass. Why don't the events fire on my NSDocument if my document subclass is the delegate for my Window?
It's not clear why you would expect NSDocument to handle -mouseDown: events for a view in a window. NSDocument doesn't respond to -mouseDown:. NSTextView (as its name suggests) is a subclass of NSView, which is a subclass of NSResponder, which does respond to -mouseDown:.
You should give the Cocoa Event-Handling Guide a good read.
It's also not clear why you want to handle the events and pass them on to views yourself. Cocoa takes care of all of this stuff for you and will likely do a far better job of it. You should clarify your overall goal (as in "why do you want to intercept clicks and forward them to views yourself?") - there may be a far better (and likely easier) way to accomplish it.

buttons in interface buider

has anyone an idea if it is possible to create ellipse or polygon buttons in IB?
Directly, not without a library.
You can, however, create an NSView subclass which performs your shape drawing for you. You will need to implement the mouse events to perform clicks correctly. Drop an NSView on your window, set its class in the inspector to your custom-drawn NSView subclass, and you're set. Check out this tutorial.
You could also create a custom control, derived from NSControl. Check out this documentation.

How to get notifications of NSView isHidden changes?

I am building a Cocoa desktop application. I want to know when a NSView's isHidden status has changed. So far using target/action doesn't help, and I can't find anything in NSNotification for this task. I would like to avoid overriding the setHidden method, because then I'll have to override all the NSView derived class that I am using.
UPDATE: I ended up using KVO. The path for "isHidden" is "hidden", probably because the setter is "setHidden".
You could use Key-Value Observing to observe the isHidden property of the NSView(s). When you receive a change notification from one of these views, you can check if it or one of its superviews is hidden with -isHiddenOrHasHiddenAncestor.
A word of warning: getting Key-Value Observing right is slightly tricky. I would highly recommend reading this post by Michael Ash, or using the -[NSObject gtm_addObserver:forKeyPath:selector:userInfo:options] method from the NSObject+KeyValueObserving category from the Google Toolbox for Mac.
More generally, one can override viewWillMoveToWindow: or the other related methods in NSView to tell when a view will actually be showing (i.e. it's window is in the window display list AND the view is not hidden). Thus the dependency on KVO for the 'hidden' key used above is removed, which only works if setIsHidden has been called on that view. In the override, 'window' (or [self window]) will indicate whether the view is being put into a visible view hierarchy (window is non-nil) or being taken out of it (window is nil).
I use it for example to start/stop a timer to update a control from online data periodically - when I only want to update while the control is visible.
Could you override the setter method for the hidden property so that it will trigger some custom notification within your application?

Resources