I really wish I could be more specific, but unfortunately, I cannot. All appropriate objects are non-nil, and all appropriate method calls are going through (presumably) successfully, but my NSPopover just never shows up. None of the delegate methods get called, either.
// ivars
NSPopover *tagPopover;
NSViewController *tagPopoverViewController;
// in method to display popover
tagPopover = [[NSPopover alloc] init];
[tagPopover setBehavior: NSPopoverBehaviorApplicationDefined];
[tagPopover setDelegate: self];
tagPopoverViewController = [[MYViewController alloc] initWithNibName: #"MYViewController" bundle: nil];
[tagPopover setContentViewController: tagPopoverViewController];
[tagPopover setContentSize: tagPopoverViewController.view.frame.size];
[tagPopover showRelativeToRect: NSMakeRect(700, 400, 5, 5) // Screen coordinates
ofView: [[NSApp keyWindow] contentView]
preferredEdge: NSMinYEdge];
Turns out the coordinate system was wrong:
NSRect theRect = [[NSApp keyWindow] convertRectFromScreen: NSMakeRect(700, 400, 5, 5)];
[tagPopover showRelativeToRect: theRect // Window Coordinates
ofView: [[NSApp keyWindow] contentView]
preferredEdge: NSMinYEdge];
Needed to convert it to the window's coordinate system first.
I suspect that tagPopoverViewController.view.frame is NSZeroRect, since frames are set to that before they start to go onscreen (e.g. they are meaningless as they come out of xibs/storyboards). Are you sure you need that line? The documentation mentions that the contentSize is bound to the content-view's size anyway.
Related
I have a window I want to display as a modal window (OS X 10.10). I'm loading the NIB for the window and am able to set the title successfully and then display the window. But whatever I do to try to affect the window position doesn't work.
This works (part of NSWindowController sub-class):
[[self window] setTitle:title];
[[NSApplication sharedApplication] runModalForWindow:[self window]];
Here are ways with which I've tried to affect the position after setting the title:
[[self window] setFrameOrigin: NSMakePoint(200.0, 200.0) ];
[[self window] setFrameTopLeftPoint: NSMakePoint(200.0, 200.0) ];
[[self window] setFrame: NSMakeRect(200, 300, [[self window] frame].size.width, [[self window] frame].size.height) display:YES];
(I've tried other values as well - just for testing, but nothing.)
I can even query the
[[self window] frame]
and it pretends to accept the new values, but the window stubbornly keeps showing up in the same position.
What gives?
I have solved this by
1- Make a new NSWindow subclass, overriding the center method, where you just make the frame of the new window positioned at whatever NSPoint you want:
class CenteredInParentWindow: NSWindow {
var parentMinX : CGFloat?
var parentMinY : CGFloat?
override func center() {
guard let parentMinX = parentMinX, let parentMinY = parentMinY else {
super.center()
return
}
self.setFrameOrigin(NSPoint(x: parentMinX, y: parentMinY))
}
}
2 - Set WindowController's window class to be the new NSWindow subclass, in Storyboard.
3- Instatiate the window controller and set the attributes of the subclassed window
let myWindowController = self.storyboard!.instantiateController(withIdentifier: "windowID") as! PlansWindowController
if let customWindow = myWindowController.window as? CenteredInParentWindow {
customWindow.parentMinX = NSApplication.shared.mainWindow?.frame.minX
customWindow.parentMinY = NSApplication.shared.mainWindow?.frame.minY
}
NSApp.runModal(for: myWindowController.window!)
}
You may need runModal method for making the window a modal one. Don't forget to include NSApp.stopModal() in the windowWillClose method which is available in NSWindowDelegate in your View controller
I'm creating a window containing an NSButton (both window content view & button have wantsLayer = YES), and setting the NSButton's shadowColor, shadowRadius, shadowOpacity and shadowOffset. But my shadow gets clipped to the rect of the NSView. How do I fix this?
NSRect wdBox = NSMakeRect(0,0,100,100);
NSWindow * theWindow = [[[NSWindow alloc] initWithContentRect: wdBox styleMask: NSTitledWindowMask | NSClosableWindowMask | NSMiniaturizableWindowMask | NSResizableWindowMask backing: NSBackingStoreBuffered defer: NO] autorelease];
NSView* cv = [[[NSView alloc] initWithFrame: wdBox] autorelease];
cv.wantsLayer = YES;
[cv setLayerUsesCoreImageFilters: YES];
theWindow.contentView = cv;
[theWindow setCollectionBehavior: NSWindowCollectionBehaviorFullScreenPrimary];
[theWindow setTitle: #"foo"];
NSButton* mView = [[NSButton alloc] initWithFrame: NSMakeRect(10, 10, 100, 80)];
[mView setLayerUsesCoreImageFilters: YES];
[mView setWantsLayer: YES];
mView.layer.masksToBounds = NO;
[mView.layer setShadowColor: [NSColor.redColor CGColor]];
[mView.layer setShadowOffset: CGSizeMake(4, 4)];
[mView.layer setShadowRadius: 8];
[mView.layer setShadowOpacity: 1.0];
[mView setBezelStyle: NSRoundRectBezelStyle];
[mView setTitle: #"bar"];
[theWindow.contentView addSubview: mView];
Here's a picture:
I had similar code before and it used to work until 10.9, but the shadow suddenly got clipped to the view, so I rewrote it to this simple case, but it's still clipped.
There is a bug in Mavericks. If you create an NSButton in XIB and give it a layer and set its shadow it works fine, but if you create one programmatically it clips its shadow.
I suspect the AppKit team did some crazy hacks when they made buttons do fast layer compositing in Mavericks (they won't redraw their backgrounds unless necessary now, for instance), because they tried to make it all happen magically, which is always always a bad idea.
Note that if you make an NSTextField the shadow code works as you'd expect. It's just NSButtons (so far) that I've found have this issue.
Please file a radar.
Let me rephrase the question:
How can I keep a child NSWindow (added to a main NSWindow using addChildWindow) the same size as the main window?
Old question:
I have two NSWindows: an uiWindow and an openglWindow. The uiWindow is borderless and initially the same size as the openglWindow. I add it as a child window. I would like to make the uiWindow follow any size changes that the openglWindow undergoes. To do that, I've subscribed to the delegate of openglWindow and I'm listening to the windowWillResize method. However, I'm confused now. I don't know which function to call on uiWindow to resize it. There are quite a few options:
setFrame: display:
`
contentRectForFrameRect
frameRectForContentRect
Below is the initialization code for the child window.
NSRect uiWindowRect = [self.openglWindow convertRectToScreen:((NSView*)self.openglWindow.contentView).bounds];
NSWindow* uiWindow = [[NSWindow alloc] initWithContentRect:uiWindowRect styleMask:NSBorderlessWindowMask backing:NSBackingStoreNonretained defer:NO];
[self.openglWindow addChildWindow:uiWindow ordered:NSWindowAbove];
I found a way that does exactly what I was looking for. Here's the code for reference:
-(void)windowDidResize:(NSNotification *)notification{
CGRect frame = CGRectMake(self.openglWindow.frame.origin.x,self.openglWindow.frame.origin.y, ((NSView*)self.openglWindow.contentView).frame.size.width, ((NSView*)self.openglWindow.contentView).frame.size.height);
[self.window setFrame:frame display:YES];
[self.window setFrameOrigin:NSMakePoint(self.openglWindow.frame.origin.x,self.openglWindow.frame.origin.y)];
[self.window viewsNeedDisplay];
}
I know how to do this in iOS but cannot figure it how to it in Cocoa.
I want to capture keyboard events and I think I need to override the acceptsFirstResponder method to accomplish that (keyDown method being triggered). So I created a class extending NSCustomView and tried to add it in the main Window but I just cannot understand how to do it. So far I added a Custom View to the main View then tried to add it programmatically like:
TestView *view = [[TestView alloc] init];
[[_window contentView] addSubview:view];
but this is not working. So how can I do this?
To see if the view has been added to a window, you can override the view's viewDidMoveToWindow method and log the value of [self window] to check (if it's nil then the view has been removed from a window):
- (void)viewDidMoveToWindow
{
NSLog(#"window=%p", [self window]);
[super viewDidMoveToWindow];
}
You should be subclassing NSView, not NSCustomView, and initWithFrame is the designated initializer for NSView, not init.
Try:
TestView *view = [[TestView alloc] initWithFrame:NSMakeRect(0, 0, 100, 200)];
[[_window contentView] addSubview:view];
so i have a quick question i have the method below which sets the alpha value of a window depending on the value from a slider, however the content of the window also becomes translucent and eventually disappears with the window.
Is there a way to just change the alpha value of the window and not the content view inside it?
- (IBAction)changeTransparency:(id)sender {
// Set the window's alpha value. This will cause the views in the window to redraw.
[self.window setAlphaValue:[sender floatValue]];}
Thanks, Sami.
Apple's docs gives a way to do this. The key is to set the window's backgroundColor's alpha to the desired value. You must also make sure to set the window's opaque property to NO (which is YES by default.)
e.x.
// At some point in your code...
[window setOpaque:NO];
// Then in your changeTransparency: method...
NSColor *backgroundColor = [window backgroundColor];
backgroundColor = [backgroundColor colorWithAlphaComponent:[sender floatValue]];
[window setBackgroundColor:backgroundColor];
Here is another way.
Suppose,
self.window <--- base view and this alpha will be changed (but exacatly fake).
subView1, subView2 <-- these views are contents of self.window. and theier alpha should not be changed.
self.window.backgroundColor = [UIColor clearColor];
UIView* anAlphaView = [[UIView alloc] initWithFrame:CGRectMake(0, 0, self.window.frame.size.widht, self.window.frame.size.height)];
anAlphaView.backgroundColor = [UIColor blackColor]; // as you want
anAlphaView.alpha = 0.5f; // as you want.
[self.window addSubview:anAlphaView];
[anAlphaView release];
[self.window addSubview:subView1]; // you should add sub views to self.window
[self.window addSubview:subView2];
You can make a method using above code :)