Multiwindows problem, cocoa - cocoa

I have a simple application, not document-based. I want to have a login window that allows people to login or add a user, and when they logged in successfully I want it to load the main page. If from the main page you click log out, it should destroy the main page and take you back to login page.
sounds like a simple plan, but for some reason I have a problem.
The way I have it right now, I check if the customer logged in or not in the main file AppDelegate and load different window controller. When customer logs in, I send a notification back to AppDelegate from Login Conntroller and load another window controller for main window.
Something like this:
if([[settings get:#"isLoggedIn"] isEqualToString:#"Yes"])
{
MainController *tmpMainController = [[MainController alloc] initWithWindowNibName:#"MainWindow"];
self.mainController = tmpMainController;
NSWindow *mainWindow = [tmpMainController window];
[mainWindow makeKeyAndOrderFront:self];
[tmpMainController release];
} else {
LoginController *tmpViewController = [[LoginController alloc] initWithWindowNibName:#"LoginWindow"];
self.loginController = tmpViewController;
loginWindow = [tmpViewController window];
[loginWindow makeKeyAndOrderFront:self];
[tmpViewController release];
}
Everything works fine, it displays the correct window. But the weird part happens when I log out from the main page, log in again and log out again. If I do it several times, instead of showing me 1 login window, it draws 2. If I continue the login process, on the second try I get 2 main windows. If I log out again, I see 4 cascade login windows, then I see 5 or 7 main windows. After all windows gets loaded all extra windows start getting destroyed one-by-one. It looks like when new window gets created it draws all old windows, then the new one and then destroys all old ones. I don't know why it happens. Would like some help.
Here is the code from my main controller when customer clicks log out:
-(IBAction)logOutClick:(id) sender
{
[settings set:#"isLoggedIn" value:#"No"];
[[self window] orderOut:self];
[[NSNotificationCenter defaultCenter] postNotificationName:#"NSUserLoggedOutNotification" object: self userInfo: nil];
}
the same thing for login controller:
if ([users verifyUser]) {
[settings set:#"isLoggedIn" value:#"Yes"];
[loginView removeFromSuperview];
[[self window] orderOut:self];
[[NSNotificationCenter defaultCenter] postNotificationName:#"NSUserLoggedInNotification" object: self userInfo: nil];
}
I have "Released when closed" checked off for both windows.

I added new nsnotification center observer every time I log out.
That was the problem.

Related

Autohide Toolbar only in full screen mode in Cocoa

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.

xcode presentModalViewController leaves old ViewController still active in background using AVCam

Have a problem with an application using AVCam. The application takes photos and saves them to the roll. Upon opening the app it takes the pictures perfectly and saves them just the way it should. However, once I leave that view controller to go to another view controller and then return, it saves images normal portrait images rotated 90 degrees. Very odd activities indeed.
After lots of hair pulling, I thought maybe I'm trying to run too many sessions, so I make sure I end the sessions in viewdidload and the viewdiddisappear:
-(void)viewDidUnload{
if([[self captureManager] session ]){
[[[self captureManager] session] stopRunning];
self.captureManager = nil;
}
}
-(void)viewDidDisappear:(BOOL)animated{
if([[self captureManager] session ]&&rollCancel==NO){
[[[self captureManager] session] stopRunning];
self.captureManager = nil;
}
}
Still not working. Still saving incorrectly after leaving.
I change views like so:
-(void)openMenu{
MenuViewController * vc = [[MenuViewController alloc] initWithNibName:#"MenuViewController" bundle:nil];
[self presentModalViewController:vc animated:YES];
[self dismissViewControllerAnimated:NO completion:nil];
}
Also tried:
-(void)openMenu{
[self dismissViewControllerAnimated:NO completion:^{
MenuViewController * vc = [[MenuViewController alloc] initWithNibName:#"MenuViewController" bundle:nil];
[self presentModalViewController:vc animated:YES];
[self dismissViewControllerAnimated:NO completion:nil];
}];
}
I don't need to release since I'm running on arc. This did not work as well, I wasn't even able to change screens with this option. SO, I added a timer to see if actions would keep acting after I leave the view controller:
updateTimer = [NSTimer timerWithTimeInterval: 5.0
target: self selector: #selector(autoTimer)
userInfo: nil repeats: YES];
[[NSRunLoop mainRunLoop] addTimer: updateTimer forMode: NSDefaultRunLoopMode];
-(void)autoTimer{
NSLog(#"blablabla");
}
And no big surprise, the blablabla continues to print into my console even after leaving the view controller. If I go back and forth, it will keep making new repeating timers and spam my console log.
This has got to have something to do with my application not saving correctly after returning to the screen. Which means that doing presentmodalviewcontroller does not actually remove everything from running.
Has anybody else ran into a similar issue like this or have any idea how to actually clear view controllers when switching between them?
didn't resolve the NSTimer still running in the background, but did fix the landscape thing which had nothing to do with stuff being open in the background
Found the solution here: https://stackoverflow.com/a/6335992/1688727

OS X: Show interface of application agent (UIElement)

How do I make the interface for an application that has 'Application is agent (UIElement)' set to yes reappear?
The interface shows up the first time I start the app, but if I close the window, and the click on the app's icon then nothing happens. I guess that it's because OS X is trying to start the app again, and there is some mechanism preventing that. What I would like is this:
The first click on the app's icon should launch the app and show the interface.
If the interface has been closed down (but the app is still running in the background) a subsequent click on the icon should just show the interface.
If the interface is already shown a click on the icon should simply move the window to the foreground.
Here is a way you can do it:
1) add + initialize method to your app delegate
+ (void)initialize
{
// check if there is a running instance of your app
NSArray * apps = [NSRunningApplication runningApplicationsWithBundleIdentifier:[[NSBundle mainBundle] bundleIdentifier]];
if ([apps count] > 1)
{
//post notification to it to update inteface
[[NSDistributedNotificationCenter defaultCenter] postNotificationName:#"updateInterface" object:nil];
//quit current instance of the app, coz you don't need two apps running continiously
exit(0);
}
}
2) Register your app for the notification
- (void)applicationDidFinishLaunching:(NSNotification *)aNotification
{
[[NSDistributedNotificationCenter defaultCenter] addObserver:self selector:#selector(updateInterface:) name:#"updateInterface" object:nil];
}
3) Add updateInterface method
- (void)updateInterface:(NSNotification *)aNotification
{
// handle your interface here
// ....
// move your app forward
[NSApp activateIgnoringOtherApps:YES];
}
I found the answer here: Closing Mac application (clicking red cross on top) and reopening by clicking dock icon.
- (BOOL)applicationShouldHandleReopen:(NSApplication*)theApplication
hasVisibleWindows:(BOOL)flag
{
[self.window makeKeyAndOrderFront:self];
return YES;
}

PDFViewAnnotationHitNotification not being delivered

I'm implementing a PDF viewer on the Mac and I want to let the user add annotations.
I've added a PDFAnnotationText to the page, and it appears just fine, but when the user clicks on it, the whole document is shrunk and an annotation list appears down the left side.
I want to customize this to display the annotation as a pop-up, similar to what Preview does. The PDFAnnotationText class reference says I can do this:
Each PDFAnnotationText object has a PDFAnnotationPopup object associated with it. In its closed state, the annotation appears as an icon. In its open state, it displays as a pop-up window containing the text of the note. Note that your application must do the work to put up a window containing the text in response to a PDFViewAnnotationHitNotification.
But when I add an observer for PDFViewAnnotationHitNotification, no notification is delivered when I click on the annotation.
I've contacted Apple about this, and the answer I received back was that it's a bug. A workaround is to handle the mouse click yourself, walk the annotations and look for a hit.
Something like this (code which runs in a mouseDown handler in a PDFView subclass):
NSPoint windowPoint = [self.window convertScreenToBase:[NSEvent mouseLocation]];
NSPoint viewPoint = [self convertPoint:windowPoint fromView:nil];
PDFPage *page = [self pageForPoint:viewPoint nearest:NO];
if (page != nil) {
NSPoint pointOnPage = [self convertPoint:viewPoint toPage:page];
for (PDFAnnotation *annotation in page.annotations) {
NSRect annotationBounds;
// Hit test annotation.
annotationBounds = [annotation bounds];
if (NSPointInRect(pointOnPage, annotationBounds))
{
NSLog(#"Annotation hit: %#", annotation);
}
}
}

customized backbutton working like real back button xcode

i am making a customized back button which works exactly as a real back button does. below is my code
UIButton *cusBack = [UIButton buttonWithType:101];
[cusBack setTitle:#"Back" forState:UIControlStateNormal];
[cusBack addTarget:self action:#selector(clickBack) forControlEvents:UIControlEventTouchUpInside];
self.view addSubView:cusBack;
and here is my selector:
-(void)clickBack{
PrevPage *pPage = [[PrevPage alloc] init];
[self.navigationController pushViewController:pPage animated:YES];
[pPage release];
}
actually, it works but the direction it creates is from RIGHT to LEFT. but I want the page to move from LEFT to RIGHT since it goes back to the recent page
thank you!
If you want to have the same behavior as the back button you should try another approach:
-(void)clickBack{ //Your presenting view controller will always be the last view controller on the stack.
[self.navigationController popToViewController:[self.navigationController.viewControllers objectAtIndex:self.navigationController.viewControllers.count-1] animated:YES];
}
Also, if you need to do some changes to the previous view controller, all you need to do is import it's header file and then you can use it's properties and public methods before popping the view controller.
#import "MyPreviousViewController.h"
...
-(void)clickBack{
[self.navigationController.viewControllers objectAtIndex:self.navigationController.viewControllers.count-1].title = #"Change title for previous view controller";
[self.navigationController popToViewController:[self.navigationController.viewControllers objectAtIndex:self.navigationController.viewControllers.count-1] animated:YES];
}
Currently, your back button doesn't work as the real back button does, because, instead of popping the last view controller off the stack(also releasing the objects), you actually keep pushing new view controllers on the stack. You might have some issues if this stack gets too big.

Resources