Detecting an NSColorWell's changed selection - objective-c

I have been looking at how to use an NSColorWell in my app, but there doesn't seem to be much documentation on it.
Is there any way to embed a colour picker (such as the NSColorWell/NSColorPanel) directly into my view? When clicking the well, it always presents a new colour picking window. Can this not be embedded somehow?
I have a custom NSButton class to which I am passing the colour from my NSColorWell. To do this, I am having to make the user pick a colour, then click a button to send this colour to my custom class. Is there a way of simply detecting when a new colour is selected directly from the color picker?

For problem 1, no, that's the system behaviour for an NSColorWell. Don't like it? make your own.
Problem 2 has two possible solutions.
Method 1: Connect the action from the color well to your object in IB and read the color of the color well via an outlet from your class. Any color change in the well will send a message to the selector of your choice.
Method 2: Add an object of your own as an observer to the color property
[colorwell addObserver:self forKeyPath:#"color" options:0 context:NULL];
then implement
- (void)observeValueForKeyPath:(NSString *)keyPath ofObject:(id)object change:(NSDictionary *)change context:(void *)context
Any color change will trigger that method.
Be sure to detach from the color well in dealloc or another breakdown method:
[colorwell removeObserver:self forKeyPath:#"color"]
There's a way to do it with bindings as well, but these two are fine.

For the color panel problem, I was also looking for such a thing. The closes I could find was this one on CocoaControls. Unfortunately, this only works on 10.7+ (as it uses NSPopover) and it looks like it uses a private API.

For swift 2 I use this code:
#IBOutlet weak var colorSelector: NSColorWell!
override func viewDidAppear() {
super.viewDidAppear()
self.colorSelector.addObserver(self, forKeyPath: "color", options: .New, context: nil)
}
override func viewDidDisappear(){
super.viewDidDisappear()
self.colorSelector.removeObserver(self, forKeyPath:"color")
}
override func observeValueForKeyPath(keyPath: String?, ofObject object: AnyObject?, change: [String : AnyObject]?, context: UnsafeMutablePointer<Void>) {
print("keyPath=>\(keyPath)")
if (keyPath! == "color") {
print("object=>\(self.colorSelector.color)")
}
}

Related

Cocoa - how to get currently selected object in collection view?

I have a NSCollectionView in a cocoa application.
I can get information about the currently selected object in the collection view through the following roundabout way:
NSIndexSet* index = [self.currentCollectionView selectionIndexes];
CardModel* card = [[self.currentCollectionView itemAtIndex:index.firstIndex] representedObject];
Does the NSCollectionView class have a method that returns the selected object? Or is this the preferred way to go about it?
Unlike NSTableView you do not have delegates/notifications which gives notifies you about the selection. So selectionIndexes is the way to go.
I am not sure if you have set up observers for array controllers or not. But the code which you have shown is only to retrieve the selected objects. To get notified about the selection of objects you need to add observer for key path selectionIndexes (or what ever is set in IB) on array controllers.
[myArrayController addObserver:self
forKeyPath:#"selectionIndexes"
options:NSKeyValueObservingOptionNew
context:nil];
-(void)observeValueForKeyPath:(NSString *)keyPath
ofObject:(id)object
change:(NSDictionary *)change
context:(void *)context
{
if([keyPath isEqualTo:#"selectionIndexes"])
{
// This will be invoked whenever objects are selected in Collection View.
// Now collectionView selectionIndexes can be used to get the selected objects.
}
}
If you're asking if selectionIndexes is the only way to access an NSCollectionView's selection the answer is yes.
One approach is to use bindings in the xib. Set up an NSArrayController for the items to be represented by the views in the collection. In the xib, in the Bindings Inspector of the Collection View, bind the Content of the CollectionView to the collectionViewArrayController.arrangedObjects. Also bind the Selection Indexes to collectionViewArrayController.selectionIndexes. Now you can make an outlet to the array controller, say in the App Delegate, and access the selected objects there.
For example, declare a selectedCard property, also a collectionViewAC outlet property connected to thecollectionViewArrayController. Now you can get the card item(s) you wish by way of selectedObjects.
- (id)selectedCard
{
id selectedCards = [collectionViewAC selectedObjects];
if ([selectedCards count]) {
return [selectedCards objectAtIndex:0];
}
return nil;
}
Use of bindings keeps everything observed and updated.

Getting the UITextField that's associated with my observer

I've got an observer on my UItextFields. It basically looks to see if the "enabled" property has changed.
If the enabled status changes, I want a method called fade to run. Fade requires a TextField to be passed to it.
How do I use the textField associated with the Observer that sends the message?
- (void) observeValueForKeyPath:(NSString *)keyPath ofObject:(id)object change: (NSDictionary *)change context:(void *) context;
{
int new = [change objectForKey:NSKeyValueChangeNewKey];
int old = [change objectForKey:NSKeyValueChangeOldKey];
if (new != old)
{
[self fadeEnable:"requires txtField"];
}
}
Thanks
As shown in the Docs,
https://developer.apple.com/library/mac/#documentation/Cocoa/Reference/Foundation/Protocols/NSKeyValueObserving_Protocol/Reference/Reference.html, the parameter 'Object' in the method call encapsulates your textfield.
you just have to cast it and youre done.
To cast it, use:
UITextField *tf = (UITextField *)object;
Edit to address your follow-up Question:
In The case of a Gesture recognizer, you want to get the view that you added the recognizer to. The 'sender' being the recognizer itself, sender.view will get you the associated view.
None of this will ever change, because recognizers only work with VIEWS.
Now, with an observer, you do not necessarily want a view. You want an OBJECT. Thats why 'object' is of type 'id', so you can add observers to objects of arbitrary types. The downside is, of course, that you need a cast. If you dont know what type to cast to, the
-isKindOfClass:
method might be useful.
Oh, and none of this is about interface-builder versus doing stuff in code.
If you find this confusing, i suggest you go study the docs on gestureRecognizers and Key-Value-Observing. If you have specific questions, let me know, or just ask an new question ;)

Loading of different views in a single custom view based on buttons in those Custom views

In my project I have only one window, in that window I have only one Custom view, nothing other than that. In that custom view I am loading one Default view, and in that default view I have one Button over there. When that button IBAction is performed, present custom view have to go and new one have to load in the same custom view of that window.
For these I tried like this,
my app delegate is my window controller, in that I declared one global variable and written KVO for that observing when ever it's value get changed. In that observation method I am trying to load different xib's(custom view) according to my requirement based on value in that global variable.
Along with these I am having different ViewControllers to control different views. In that view controller classes I am loading new values into that global variable by using object of AppDelegate class.
-(void)observeValueForKeyPath:(NSString *)keyPath ofObject:(id)object change:(NSDictionary *)change context:(void *)context
{
NSLog(#"entered into key value observing");
if ([viewName isEqualToString:#"LoginView"]) {
NSLog(#"Dont change the current view");
} else {
NSLog(#"Load new view in customView");
[self loadNewView];
}
}
-(void)loadNewView
{
NSLog(#"entered into login in method");
[[_viewController view] removeFromSuperview];
_viewController=[[NSViewController alloc] initWithNibName:#"NewView" bundle:nil];
[self.window setContentSize:_viewController.view.frame.size];
[_customView addSubview:[_viewController view]];
NSLog(#"at final step");
}
Control is moving from ViewController class to AppDelegate and in that its entering into that KVO method also, it is executing every line as I want it to execute. But its not affecting the output result.
As per as I know, it is executing every thing in that ViewController class itself by using object of AppDelegate. So it is not affecting of loading of view in CustomView in in that window.
Will any one please suggest me some solution for solving these........
Problem is not in the code what is shown in the above question.
In the above code instance of NSViewController is creating again and again for the same purpose. Along with that try to create object of appDeligate in ViewController class by using Shared instance.

UITableViewController and UITextField keyboard

I have a UITableViewController with a grouped static UITableView. I am defining the cells for my static table view on the storyboard. One of the cells has a textfield in it. When this textfield is called, the keyboard pops up, however, the tableview is not automatically resizing like it normally would on a table view controller. So now the keyboard is partially covering the textfield and I can't scroll up.
My understanding is that when you are using a UITableViewController and a tableview, the viewable area should automatically shrink when the keyboard is called. It does work as intended in other parts of my app, just not with this static table view. Does it not work with static tables? Is there something else I am missing? Is there an easy way to solve this?
Thanks
Answer
It has nothing to do with static cells. They should work.
If your controller is already a UITableViewController, check if you used the method viewWillAppear. If you did, you have to call [super viewWillAppear:YES] to get the 'automatic behavior' to work.
-(void)viewWillAppear:(BOOL)animated {
[super viewWillAppear:YES]; // This line is needed for the 'auto slide up'
// Do other stuff
}
This problem turns up easily because the boilerplate code for the controllers don't come with the viewWillAppear method call and if you define it in your controller, you override it.
Extra Information
Look at this link.
Apple Table View Programming Guide
Note: UITableViewController has new capabilities in iOS 3.0. A
table-view controller supports inline editing of table-view rows; if,
for example, rows have embedded text fields in editing mode, it
scrolls the row being edited above the virtual keyboard that is
displayed.... blah....
The important bit
The UITableViewController class implements the foregoing behavior by
overriding loadView, viewWillAppear:, and other methods inherited from
UIViewController. In your subclass of UITableViewController, you may
also override these methods to acquire specialized behavior. If you do
override these methods, be sure to invoke the superclass
implementation of the method, usually as the first method call, to get
the default behavior.
For Swift
override func viewWillAppear(animated: Bool) {
super.viewWillAppear(animated)
}
Pushes the view up if one of the table forms is selected for editing (requires keyboard notification implementation)
- (void) keyboardDidShow:(NSNotification *)aNotification
{
[UIView beginAnimations:nil context:NULL];
[UIView setAnimationDuration:0.25];
self.view.center = CGPointMake(self.view.center.x, self.view.center.y-moveAmount);
[UIView commitAnimations];
isRaised = [NSNumber numberWithBool:YES];
}
Resizes the table (divides height by 2). Swap this into the keyboard did show method. Also, you can use the keyboard did hide method to undo this stuff.
CGRect temp = CGRectMake(mineTable.frame.origin.x, mineTable.frame.origin.y, mineTable.frame.size.width, mineTable.frame.size.height/2);
mineTable.frame = temp;

NSCollectionViewItem double-click action?

How do I set an action for when a user double clicks an NSCollectionViewItem. NSTableView, for example, has the setDoubleAction method. Is there something similar for NSCollectionView?
Thanks
I know this question is ancient, but it comes up as the third result on Google right now, and I've found a different and very straightforward method that I haven't seen documented elsewhere. (I don't just need to manipulate the represented item, but have more complex work to do in my app.)
NSCollectionView inherits from NSView, so you can simply create a custom subclass and override mouseDown. This is not completely without pitfalls - you need to check the click count, and convert the point from the main window to your collection view's coordinate, before using NSCollectionView's indexPathForItem method:
override func mouseDown(with theEvent: NSEvent) {
if theEvent.clickCount == 2 {
let locationInWindow = theEvent.locationInWindow
let locationInView = convert(locationInWindow, from: NSApplication.shared.mainWindow?.contentView)
if let doubleClickedItem = indexPathForItem(at: locationInView){
// handle double click - I've created a DoubleClickDelegate
// (my collectionView's delegate, but you could use notifications as well)
...
This feels as if I've finally found the method Apple intended to be used - otherwise, there's no reason for indexPathForItem(at:) to exist.
You'd probably want to handle this in your NSCollectionViewItem, rather than the NSCollectionView itself (to work off your NSTableView analogy).