I am developing an app in which the toolbar can be shown/hide by the user using a button. The problem is the following: If the user chooses to hide the toolbar and then enters the fullscreen mode, the toolbar is shown.
The user interface has been created programmatically (i.e. not using Interface Builder).
This is the toolbar creation in the app delegate:
mainToolbar = [[NSToolbar alloc] initWithIdentifier:MAIN_TOOLBAR];
[mainToolbar setAllowsUserCustomization:NO];
[mainToolbar setDisplayMode:NSToolbarDisplayModeIconOnly];
[mainToolbar setDelegate:self];
[window setToolbar: mainToolbar];
These are the actions performed by the buttons:
-(void)hideToolbar {
editing = YES;
[mainToolbar setVisible:NO];
}
-(void)showToolbar {
editing = NO;
[mainToolbar setVisible:YES];
}
I have tried to fix it using window delegate methods but still the toolbar is shown when entering full screen mode regardless the value of editing.
- (void)windowDidEnterFullScreen:(NSNotification *)notification {
[mainToolbar setVisible:!editing];
}
- (void)windowDidExitFullScreen:(NSNotification *)notification {
[mainToolbar setVisible:!editing];
}
Many thanks in advance!
I could not find a way to maintain the hidden/shown state of the toolbar when the window goes full screen, but you can set the toolbar to always be hidden in full screen, and to animate in when the user goes to the top of the screen. In your window delegate, you can set NSApplicationPresentationOptions to return NSApplicationPresentationAutoHideToolbar in as one of the options. Mine looks like this:
- (NSApplicationPresentationOptions)window:(NSWindow *)window willUseFullScreenPresentationOptions:(NSApplicationPresentationOptions)proposedOptions
{
return (NSApplicationPresentationFullScreen |
NSApplicationPresentationHideDock |
NSApplicationPresentationAutoHideMenuBar |
NSApplicationPresentationAutoHideToolbar);
}
Here is the relevant documentation: https://developer.apple.com/library/mac/#documentation/General/Conceptual/MOSXAppProgrammingGuide/FullScreenApp/FullScreenApp.html
Related
My goal is simple and yet I cannot find a solution in spite of lots of searching.
Basically, when my app is in full-screen (kiosk) mode, I want the toolbar only to auto-hide, but I want the menu bar hidden.
Apparently this combination is not valid. I've tried:
- (NSApplicationPresentationOptions)window:(NSWindow *)window willUseFullScreenPresentationOptions: (NSApplicationPresentationOptions)proposedOptions
{
return (NSApplicationPresentationFullScreen |
NSApplicationPresentationHideDock |
NSApplicationPresentationHideMenuBar |
NSApplicationPresentationAutoHideToolbar);
}
I get the following exception:
"... fullscreen presentation options must include NSApplicationPresentationAutoHideMenuBar if NSApplicationPresentationAutoHideToolbar is included"
Thing is, I don't want the menu bar displayed at all!
So, I'm presuming this is not possible using the standard presentation options. Any ideas how I might approach implementing this behaviour manually?
I'm thinking along the lines of: detect the mouse position and only show/hide the toolbar when the mouse is at/near the top of the screen.
I'm very new to Cocoa so not sure where I would start to achieve this. Any help much appreciated!
Many thanks,
John
I've got It to work, but only by using private APIs.
First I had to find out how to prevent the menubar from appearing. I discovered the functions _HIMenuBarPositionLock and _HIMenuBarPositionUnlock, from Carbon (link the app with Carbon.framework).
Then I had to create a custom subclass of NSToolbar, at awakeFromNib I register notification observers to lock and unlock the menubar when the window enters and exits fullscreen, respectively:
- (void)awakeFromNib
{
[super awakeFromNib];
[[NSNotificationCenter defaultCenter] addObserverForName:NSWindowWillEnterFullScreenNotification object:[self _window] queue:[NSOperationQueue mainQueue] usingBlock:^(NSNotification *note) {
// lock menubar position when entering fullscreen so It doesn't appear when the mouse is at the top of the screen
_HIMenuBarPositionLock();
}];
[[NSNotificationCenter defaultCenter] addObserverForName:NSWindowWillExitFullScreenNotification object:[self _window] queue:[NSOperationQueue mainQueue] usingBlock:^(NSNotification *note) {
// unlock menubar position when exiting fullscreen
_HIMenuBarPositionUnlock();
}];
[self _setupToolbarHotspotTrackingView];
}
_setupToolbarHotspotTrackingView is a method on SOToolbar which adds a view to the window, this view will be used to track the mouse location and show/hide the toolbar accordingly.
- (void)_setupToolbarHotspotTrackingView
{
NSView *contentView = [self _window].contentView;
self.toolbarHotspotTrackingView = [[SOToolbarTrackingView alloc] initWithFrame:contentView.bounds];
[contentView addSubview:self.toolbarHotspotTrackingView];
self.toolbarHotspotTrackingView.autoresizingMask = NSViewWidthSizable|NSViewHeightSizable;
self.toolbarHotspotTrackingView.toolbar = self;
}
I also had to override _attachesToMenuBar on SOToolbar so the animation works properly.
- (BOOL)_attachesToMenuBar
{
return NO;
}
SOToolbarTrackingView sets up a tracking area for mouse moved events and checks to see if the mouse is at the top of the window. It then calls some methods on the private class NSToolbarFullScreenWindowManager to show and hide the toolbar.
There's too much stuff to explain It all in detail here, I've uploaded my experimental project so you can take a look. Download the sample project here.
I'm playing around with an idea and basically I want a NSStatusItem with a NSPopoverController. I read about all the problem people had but I just want to try it. Is there a clean way to do it by now? All the versions I've seen are at least 1 year old and suuuuper hacky.
This was my approach so far but if I click my app in the statusbar nothing happens...
- (void)applicationDidFinishLaunching:(NSNotification *)aNotification
{
self.statusItem = [[NSStatusBar systemStatusBar] statusItemWithLength:NSVariableStatusItemLength];
//[self.statusItem setView:view];
[self.statusItem setTitle:#"Test"];
[self.statusItem setHighlightMode:YES];
[self.statusItem setAction:#selector(activatePopover:)];
}
-(IBAction)activatePopover:(id)sender
{
BOOL isEnabled = NO;
if (isEnabled) {
[self.popover showRelativeToRect:NSMakeRect(0, 0, 50, 50) ofView:statusItem.view preferredEdge:NSMinYEdge];
} else {
[self.popover close];
}
}
Any ideas how to get this running?
Thanks
This will not work without using a custom view on the status item. If you don't set a custom view, the view property will be empty (it only returns custom views, not whatever view NSStatusItem uses internally when you just use setTitle).
Unfortunately, as per Apple's docs, you'll need to provide your own view and handle clicks yourself if you want to use NSPopover.
I haven't seen a complete example that encompasses correct handling of this (the default implementation of status items does rather a lot which you will have to do all manually), and also fixes popover wonkynesses:
NSPopover, by default, won't become the key window (some controls won't work), unless you overwrite canBecomeKeyWindow of NSPopover's window
Correctly dismissing menus of other status items (you can call popUpStatusItemMenu with an empty menu to correctly focus your status item)
Drawing the highlighted background with drawStatusBarBackgroundInRect
Reacting to both left and right mouse clicks
Using NSRunningApplication.currentApplication.activateWithOptions to make sure all windows of your status item become active (otherwise your popover will, erratically, not be the receiver of keyboard input)
Dismissing the NSPopover with NSEvent.addGlobalMonitorForEventsMatchingMask (the built-in dismissal mechanism popovers come with doesn't work with status items)
Removing the status item on termination with NSStatusBar.systemStatusBar.removeStatusItem
I hope to have a blog post about this out sometime soon (note: I'm using RubyMotion, not Objective-C), that explains all these issues and hopefully provides an easier base to create menulets. I'll update this comment if I write that post.
Code:
-(void)initializeStatusBarItem
{
self.statusItem = [[NSStatusBar systemStatusBar] statusItemWithLength:NSSquareStatusItemLength];
NSImage* image = [NSImage imageNamed:#"image"];
// [image setTemplate:YES];
self.statusItem.button.image = image;
self.statusItem.highlightMode = NO;
self.statusItem.button.action = #selector(statusBarItemDidClick:);
}
- (void)statusBarItemDidClick:(NSStatusBarButton *)sender{
MainViewController *mainView = [[MainViewController alloc] init];
self.popoverView = [[NSPopover alloc] init];
[self.popoverView setContentViewController:mainView];
self.popoverView.contentSize = CGSizeMake(300, 400);
self.popoverView.behavior = NSPopoverBehaviorTransient;
[self.popoverView showRelativeToRect:sender.bounds ofView:sender preferredEdge:NSMaxYEdge];
}
I added a childWindow of a custom subclass of NSWindow to a parentWindow (also a custom subclass of NSWindow). The childWindow has the NSBorderlessWindowMask and canBecomeKeyWindow: is overridden to return YES and canBecomeMainWindow: to return NO.
The childWindow is set to resize with the parentWindow. So I want to create the illusion that the views of the childWindow are part of the parentWindow. The main idea is to arrange the document windows created by the document-based application within a main window to provide a tabbed interface (just like in a browser) to switch between the documents.
My problem is that whenever I click in one of the views of the childWindow, the parentWindow (the main window) looses focus and the traffic light buttons are getting greyed out. This is obviously contrary to what I want to achieve.
I found this answer:
Make NSView in NSPanel first responder without key window status
But even if I override isKeyWindow: (of the main window) to always return YES, the title bar gets greyed out nonetheless when I click into the childWindow.
I also tried to follow this advice:
http://www.cocoabuilder.com/archive/cocoa/143945-non-focused-child-window.html
But I'm not sure what "include the child window in its responder chain just ahead of its nextResponder" means. With canBecomeKeyWindow: to return NO (for the childWindow), the views within the child never can become key and are always greyed out.
Any clue what I am doing wrong?
One addition: Is it possible to make the views in the childWindow FirstResponder without giving the childWindow key-status?
I got this working by mimicking the behaviour of NSPopover. On investigation I found that a popover (which uses a private NSPanel subclass "_NSPopoverWindow") believed it was the key & main window, even though it is not the window returned from [NSApp keyWindow].
Create your own custom NSPanel subclass, attach it to the parent window, and then override the following methods as so:
- (BOOL)isKeyWindow {
return YES;
}
- (BOOL)isMainWindow {
return YES;
}
- (BOOL)canBecomeKeyWindow {
return YES;
}
- (BOOL)canBecomeMainWindow {
return YES;
}
- (void)makeKeyWindow {
[super makeKeyWindow];
[self.parentWindow makeKeyWindow];
}
- (void)makeMainWindow {
[super makeMainWindow];
[self.parentWindow makeMainWindow];
}
- (void)becomeKeyWindow {
[super becomeKeyWindow];
}
- (void)becomeMainWindow {
[super becomeMainWindow];
[self.parentWindow becomeMainWindow];
}
- (void)resignMainWindow {
}
- (void)resignKeyWindow {
}
I have a PopoverController view that allows a user to download a file. On button press, the popOver view will expand in size, display download status, and the main view controller will be obscured by an unhidden "cover" view that has been added to the PopoverController's "passThroughViews" property so that the user can not accidentally dismiss the pop over while the file is downloading.
My problem is that, in storyboards, my main viewController is embedded in a Navigation Controller. I can't seem to cover the navigation controller's bar with a view in the storyboard, and if the user presses anywhere on the navigation bar then the popover will disappear and the user will lose the download's progress bar.
How do I either cover up the navigation bar with my "cover" view, or how do I add the navigation bar's view to my popOverController's passThroughViews?
Opening the Popover from the main viewController:
- (IBAction)openDataOptionsPopOver:(id)sender
{
UIStoryboard *storyboard = [UIStoryboard storyboardWithName:#"MainStoryboard" bundle:nil];
PopOverViewController *optionsWindow = [storyboard instantiateViewControllerWithIdentifier:#"dataOptions"];
self.popUp = [[UIPopoverController alloc] initWithContentViewController:optionsWindow];
[self.popUp setDelegate:self];
[nextNavButton setEnabled:NO]; //Disabling barButtonItem on the navigationController
optionsWindow.containerPopOver = self.popUp; //Pointer to the popover, to resize it later.
optionsWindow.coverView = self.coverView; //Pointer to the coverView, to (un)hide later
[popUp presentPopoverFromRect:[sender frame] inView:[sender superview] permittedArrowDirections:UIPopoverArrowDirectionDown animated:YES];
}
Setting the passThroughViews property inside of the PopoverViewController:
//Expands the popOver on press of "refreshFileButton" to display progressView
-(void) explodeWindow
{
//setting self.navigationController.view and ...visibleViewController.view here didn't seem to work ...
[containerPopOver setPassthroughViews:[NSArray arrayWithObjects:coverView, nil]];
[containerPopOver setPopoverContentSize:CGSizeMake(600, 400) animated:YES];
[titleBarItem setTitle:#"Downloading File. Please Wait ..."];
[refreshFileButton setHidden:YES];
[progressView setHidden:NO];
[downloadLabel setHidden:NO];
[coverView setHidden:NO];
[progressView setProgress:0.0 animated:NO];
}
I've tried adding self.navigationController.view to passThroughViews with no success--it actually turns out to be a null pointer. And I can't seem to place a UIView at any level in storyboards that will cover all my controls without obscuring the popOver. What am I missing here? And thanks for reading.
Edit:
As Aglaia points out below out, implementing the following, and avoiding passThroughViews, is probably the best way to do this.
- (BOOL)popoverControllerShouldDismissPopover:(UIPopoverController *)popoverController
{
//Don't dismiss our popover when the view covering our controls is present
if([coverView isHidden]){
return YES;
}else{
return NO;
}
}
Maybe there is something I am missing, but why don′t you just implement a new view controller with its navigation bar set to none and present it modally on button press? Then when the download is finished you just dismiss the view controller.
If you want the user to see the underlying view you can use a UIAlertView instead.
Alternatively set you view controller as the delegate of the popover controller and forbid the user to dismiss your popover on touch outside through
- (BOOL) popoverControllerShouldDismissPopover:(UIPopoverController *)popoverController
{
return NO;
}
Then when you want to dismiss it call dismissPopoverAnimated:
to cover the whole screen including navigation bar:
[myView setFrame:[[UIScreen mainScreen] bounds];
[self.navigationController.view addSubview:myView];
I have a webView in my cocoa application (macosx not iphone), it displays some javascript to the user but I don't want the user to be able to select any of the text or right click and select the reload option.
Is there a simple way to disable all interaction with the webView?
I know on the iPhone its easy to disable user interaction but I have never needed to do this on the desktop before and can't find a solution.
Set both editing and UI delegations:
[view setUIDelegate:self];
[view setEditingDelegate:self];
Then add the methods above to disable text selection and context menu.
- (NSArray *)webView:(WebView *)sender contextMenuItemsForElement:(NSDictionary *)element
defaultMenuItems:(NSArray *)defaultMenuItems
{
// disable right-click context menu
return nil;
}
- (BOOL)webView:(WebView *)webView shouldChangeSelectedDOMRange:(DOMRange *)currentRange
toDOMRange:(DOMRange *)proposedRange
affinity:(NSSelectionAffinity)selectionAffinity
stillSelecting:(BOOL)flag
{
// disable text selection
return NO;
}
via the UI delegation it might be useful also to disable drag and drop operations, add
- (NSUInteger)webView:(WebView *)sender dragSourceActionMaskForPoint:(NSPoint)point
{
return WebDragSourceActionNone; // Disable any WebView content drag
}
- (NSUInteger)webView:(WebView *)sender dragDestinationActionMaskForDraggingInfo:(id <NSDraggingInfo>)draggingInfo
{
return WebDragDestinationActionNone; // Disable any WebView content drop
}