When loading a NSView onto a NSPanel, do I have to manually set focus to the view? - objective-c

I have a NSPanel in my MainMenu.xib called filePanel, it has a NSView called filePanelView. When buttons are clicked, I will load a NSView from a nib and add it to the filePanel like this:
[NSApp activateIgnoringOtherApps:YES];
[filePanel setIsVisible:YES];
[filePanelView addSubview:[fileBrowseViewController browseView]];
The problem is that the controls on these subviews can never be brought into focus. For example, a NSTextfield that is set to be editable is not editable, and doesn't have a focus ring around it when clicked. I can click buttons though. But on another window, a determinate progress bar has it's progress measured in grey instead of blue. Is there something I'm doing wrong in terms of needing to manually set these views as having focus?

Related

Can't pass UIPickerView selection to UITextView in another UIViewController

I have a UIViewController with a UITextView and UIButton in it. When the user clicks this button it will present a PopUp created with a UIViewController. When the Popup view loads, it displays a UIPickerView.
The problem I am having is passing the selection from the picker to the textView in the first view. I know it has to do with creating global variables, but I just can't figure out how to do it.
Anyone could help?

NSPanel Sheet Not Receiving Mouse Events

SIMPLIFIED:
I have the following:
NSWindow (this is the main window of the app)
NSPanel (this is presented as a sheet, over the main NSWindow, using [NSApp beginSheet:)
The main NSWindow has a subclassed NSButton that uses mouse events, including mouseEntered, mouseExited. This changes the background color of the button as I hover over it.
I present my NSPanel, this also has an NSButton of the same subclass, to change color on hover.
Mouse events still occur in the main NSWindow while my NSPanel is presented. I can tell this, as hovering over the buttons still works.
However, hovering over the buttons in the presented NSPanel does not work, despite them being no different from the other buttons. I can click on them and they receive an action. But they do not call the mouse events.
ORIGINAL:
I have a simple setup in my Mac app. I have a main NSWindow, as any usual mac app, then I run the following code which displays my detail view.
My detail view is an NSWindowController subclass and I am displaying it as a sheet. This is setup as an NSPanel in the xib and just displays a detail view for me over my main window.
APDetailWindowController *vc = [[APDetailWindowController alloc] initWithWindowNibName:#"APDetailWindowController"];
self.detailWindowController = vc;
self.detailWindowController.delegate = self;
APAppDelegate *appDelegate = [[NSApplication sharedApplication] delegate];
[NSApp beginSheet:self.detailWindowController.window modalForWindow:appDelegate.window modalDelegate:self didEndSelector:nil contextInfo:nil];
In my main window (of the AppDelegate), I have my views played out as any app, including some NSButtons. These buttons are subclassed so that when they receive mouseEntered and mouseExited events, they change colour. Basically creating a rollover effect.
In my detail view, I also have buttons of the same subclass. However, they are not receiving the mouse events at all. I can see when moving my mouse over them nothing happens. Even when the detail view is displayed, the rollover works on the main window buttons in the background, but not in my detail view.
I am thinking maybe some sort of focus is still on the main window and needs to be set to the detail window?

Show NSWindow on right click in NSTableView

I'd like to display an NSWindow when right clicking an item in an NSTableView, similarly to how the available outlets are shown in Interface Builder when you right click an object:
Unfortunately you can only use an NSMenu subclass as the menu property.
I also didn't find a delegate method of NSTableView that notifies about right clicks.
I was able to subclass NSTableView and implement rightMouseDown: and rightMouseUp: to be notified about those events, but if I set the menu property of the row cells to nil, they are not highlighted when right clicked, even though I call the super implementation):
- (void)rightMouseDown:(NSEvent *)theEvent {
[super rightMouseDown:theEvent];
NSPoint eventLocation = [theEvent locationInWindow];
eventLocation = [self convertPoint:eventLocation fromView:nil];
NSInteger rowIndex = [self rowAtPoint:eventLocation];
NSLog(#"Right clicked at row index %d", rowIndex);
}
I would like to have the highlight effect in the image below but display a window instead of the context menu:
First for the right click: explicitly select the row on right click (e.g. via this message). Then create your own NSWindow descendant, set an own NSView class as contentView and in the view you can draw the black background, rounded borders and what not. Show this window in your right click handler.
You can use an NSPopover, which works quite nicely. A popover creates a window for you, even if it is somewhat hidden. You'll get it from your controls if you send them the window message, and can register to listen for events, for instance.
The whole popover can be created in IB, and just have to implement the showRelativeToRect:ofView:preferredEdge: method in code.
To catch the right click event, you can use rightMouseDown:, which is originally defined in NSResponder, but is overridden in NSView to simply catch the event and show menu and it doesn't pass the event upwards in the responder chain (or the inheritance chain, for that matter). Hence, you simply implement that method to call showRelativeToRect:ofView:preferredEdge:.
You will typically need to have the contents in an NSViewController and its own accompanying nib file.
The NSPopover's contentViewController property can be set in IB, too.
All in all, not much code needed.
This tutorial is useful.

What's going on with my UIButtons in a xib?

I have a set of UIButtons (defined in a xib) who have labels that need to be updated periodically. In the ViewDidLoad method of the view controller of those buttons' superview, I have an update method that does, for each button:
button.titleLabel.text = #"Relevant Text";
[button setNeedsDisplay];
and when you tap a button, another method runs which pops up a UIAlertView, which in turn calls back a method on the view controller which does much the same thing as the initial text setting method:
button.titleLabel.text = #"New Text";
[button setNeedsDisplay];
however, this code simply isn't working, the button label's text doesn't get updated in either method, it remains a blank white button. In the xib I don't define any text on the buttons - there's no point, the button text doesn't make sense unless it's set at runtime. Anyway, on a lark, I decided to set the text of one of the buttons to "test test test".
Now, when I tap that particular button, it pops up the UIAlertView but in the background changes the text of the button to "test test test test". And this time, the UIAlertView callback does what I expect it to and sets the text for only that button. When I hit it again, the text goes back to "test test test test" until I dismiss the UIAlertView, which again will run the callback method and set the button text to whatever the method should.
I have no idea what's going on here, or why setting the text initially in the xib has any relation to whether or not I can set that text later programatically. Obviously this isn't the behavior I want, I want to know how to for sure set the text on the buttons.
Edit: SVD's advice about setTitle:ForState: solved my problem, thanks. I'm still curious though as to why the title label set in the .xib shows up, but only when I have a UIAlertView pop up.
You may need to use [setTitle: forState:] to set the button title for normal and highlighted (or selected) state.
(And do make sure the button is connected to the outlet, as jtbandes points out).

close UIPopover on rotation with a fadeout animation

The Apple Pages and Numbers apps have popovers (for "tools" etc) that close with a lovely fade out effect when you rotate the device. I'm trying to recreate this, but my popovers always seem to close instantly, so the animation of the rotation doesn't look quite as smooth. I'm currently using:
- (void)willAnimateRotationToInterfaceOrientation:(UIInterfaceOrientation)interfaceOrientation duration:(NSTimeInterval)duration
{
[toolsPopoverController dismissPopoverAnimated:YES];
}
Does anyone know the best way to achieve the same effect seen in Pages/Numbers?
Thanks!
Based on the documentation for the UIPopoverController (emphasis added):
If the user rotates the device while a popover is visible, the popover controller hides the popover and then shows it again at the end of the rotation. The popover controller attempts to position the popover appropriately for you but you may have to present it again or hide it altogether in some cases. For example, when displayed from a bar button item, the popover controller automatically adjusts the position (and potentially the size) of the popover to account for changes to the position of the bar button item. However, if you remove the bar button item during the rotation, or if you presented the popover from a target rectangle in a view, the popover controller does not attempt to reposition the popover. In those cases, you must manually hide the popover or present it again from an appropriate new position. You can do this in the didRotateFromInterfaceOrientation: method of the view controller that you used to present the popover.
It would appear that by calling [toolsPopoverController dismissPopoverAnimated:YES] in the willAnimateRotationToInterfaceOrientation: method, you are dismissing with an animation while the popover is hidden during the rotation transition.
If you call the dismissPopoverAnimated:YES method in the didRotateFromInterfaceOrientation: method instead, the default behavior with the popover in the new position should present before the dismiss animation is invoked.
If the default animation is still not what you are looking for at this point, I would create a custom animation block and manage the fadeout or re-sizing explicitly to meet your desired needs.
This worked for me by calling dismissPopoverAnimated: from willRotateToInterfaceOrientation:duration:.