How to restore keyWindow (NSView - Appkit) - objective-c

I have some code which opens a modal window (itself composed from a few views) and makes it closed when we click anywhere on it.
here is some part of code:
int myFunc()
{
// Create views
NSPanel *panel = ...;
CustomNSTextView * textView = ... ;
CustomNSImageView * imageView = ...;
[view addSubview:textView];
[view addSubview:imageView];
[panel setContentView:view];
[[NSApplication sharedApplication] runModalForWindow:panel];
NSView* parentView = [view superview];
[[parentView window] makeFirstResponder:parentView];
[textView release];
[imageView release];
[view release];
[panel release];
}
#implementation CustomNSTextView : NSTextView
- (void) mouseDown:(NSEvent *)theEvent
{
#pragma unused(theEvent)
[[NSApplication sharedApplication] stopModal];
}
#implementation CustomNSImageView : NSImageView
- (void) mouseDown:(NSEvent *)theEvent
{
#pragma unused(theEvent)
[[NSApplication sharedApplication] stopModal];
}
The bug is after the window is closed, the application (that launched the modal window), won't receive any key event anymore (while it still was as the modal window was still open).
Only after I refocus on the application, it will receive key events again.
Please anyone gives me some idea, I could not find something relevant on the net.
Thanks
Nathaniel

Related

Custom NSMenuItem-views glitch when scrolling NSMenu on low resolution screen

I'm having some glitchy bugs when I'm using my menu bar application on a low resolution screen. Please see screenshots below as illustration! Because of the reduced height of the lo-res screen my total NSMenu does not fit in the screen, and so automatically a scrollview appears so i can still get to all the different items.
The problem is that my cutsom NSMenuItems, which thus have custom NSViews with their own draw [drawRect:(NSRect)dirtyRect] method, are behaving glitchy when scrolling down, and back up. It's as if there are some drawing calls missing. I unfortunately do not know how to detect when the NSMenu is effectively scrolling, or simply to access this automagically created scrollview on the NSMenu class...
Any help much appreciated !
the drawing code of my view in my NSMenuItem.
- (void)drawRect:(NSRect)dirtyRect
{
if (self.enabled)
{
if (self.isHighlighted)
{
[self colorRectHighlighted:dirtyRect];
[self drawTextInRect:dirtyRect withState:CustomCheckMenuItemViewStateHiglighted];
[self drawStateIconWithColor:[NSColor whiteColor]];
}
else
{
[self drawTextInRect:dirtyRect withState:CustomCheckMenuItemViewStateNormal];
AppDelegate *appDelegate = (AppDelegate *)[[NSApplication sharedApplication] delegate];
if(appDelegate.menuIsInDarkMode)
{
[self drawStateIconWithColor:[NSColor colorWithWhite:1.0 alpha:0.9]];
}
else
{
[self drawStateIconWithColor:[NSColor selectedMenuItemIndicatorColor:([NSColor currentControlTint]==NSBlueControlTint)?YES:NO]];
}
}
}
else
{
[self drawTextInRect:dirtyRect withState:CustomCheckMenuItemViewStateNormal];
AppDelegate *appDelegate = (AppDelegate *)[[NSApplication sharedApplication] delegate];
if(appDelegate.menuIsInDarkMode)
{
[self drawStateIconWithColor:[NSColor colorWithRed:136/256.0 green:136/256.0 blue:136/256.0 alpha:0.9]];
}
else
{
[self drawStateIconWithColor:[NSColor colorWithWhite:0 alpha:0.1]];
}
}
}
EDIT:
I initialise the NSMenuItems with the sliders as this:
self.tiltSliderViewController = [[SlidingViewController alloc] initWithNibName:#"SlidingViewController" bundle:nil];
self.tiltSliderViewController.delegate = self;
[tiltSliderMenuItem setView:[self.tiltSliderViewController view]];
self.rotationSliderViewController = [[SlidingViewController alloc] initWithNibName:#"SlidingViewController" bundle:nil];
self.rotationSliderViewController.delegate = self;
[rotationSliderMenuItem setView:[self.rotationSliderViewController view]];
The viewcontrollers used here have the following implementation:
#interface SlidingViewController ()
#end
#implementation SlidingViewController
- (IBAction)valueChanged:(id)sender
{
float slider_value = [_slider floatValue];
[_slider setToolTip:[NSString stringWithFormat:#"%ld",
_slider.integerValue]];
[_delegate sliderValueChanged:slider_value sender:self];
}
#end
screenshots:
NORMAL
DOWN
UP AGAIN = GLITCH!!

what's the difference between self.view.widow and [[UIApplication sharedApplication] keyWindow] in Objective-C

if I do like this
- (void)viewDidLoad {
[super viewDidLoad];
UIAlertView *alert = [[UIAlertView alloc] initWithTitle:nil message:#"test" delegate:self cancelButtonTitle:#"ok" otherButtonTitles:nil];
[alert show];
}
// Called when a button is clicked. The red view and alert will be automatically dismissed after this call returns
- (void)alertView:(UIAlertView *)alertView clickedButtonAtIndex:(NSInteger)buttonIndex {
[self showView];
}
// after animation ,this will work fine
- (void)alertView:(UIAlertView *)alertView didDismissWithButtonIndex:(NSInteger)buttonIndex {
// [self showView];
}
- (void)showView {
// Called when a button is clicked. The view will be automatically dismissed after this call returns
UIView *view = [[UIView alloc] init];
view.frame = CGRectMake(0, 0, 200, 200);
view.backgroundColor = [UIColor redColor];
view.center = CGPointMake([UIScreen mainScreen].bounds.size.width/2.0f, [UIScreen mainScreen].bounds.size.height/2.0f);
// [self.view.window addSubview:view]; //when i use this ,both of the two delegate methods works fine
UIWindow *window = [[UIApplication sharedApplication] keyWindow];
[window addSubview:view];
}
Then the red view and alert will be automatically dismissed after alertView:clickedButtonAtIndex: call returns
who can show me the reason?
what's the difference between self.view.widow and [[UIApplication sharedApplication] keyWindow] in Objective-C
Your question is essentially addressed here:
diffrence between [[[[UIApplication sharedApplication] delegate] window] and [[UIApplication sharedApplication].keyWindow?
To tie it back to your question, the UIAlertView gets presented in a system window, and it is this window that is the keyWindow while your alert is being shown. When the alert is dismissed, that window disappears, hence why your view disappears.
Assuming that self.view is a subview of the app's window (almost always the case), your view continues to display because the window itself continues to be displayed after the alert is dismissed.

keyDown not being called

I have a custom NSView called SurfaceView. It is the contentView of a NSWindow and it handles basic events like mouse click and drawing. But don't matters what I do, it does not handle the keyDown function. I've already override the acceptsFirstResponder but nothing happens.
If it matters, I run the application using a custom NSEvent loop, shown below:
NSDictionary* info = [[NSBundle mainBundle] infoDictionary];
NSString* mainNibName = [info objectForKey:#"NSMainNibFile"];
NSApplication* app = [NSApplication sharedApplication];
NSNib* mainNib = [[NSNib alloc] initWithNibNamed:mainNibName bundle:[NSBundle mainBundle]];
[mainNib instantiateNibWithOwner:app topLevelObjects:nil];
[app finishLaunching];
while(true)
{
NSEvent* event = [app nextEventMatchingMask:NSAnyEventMask untilDate:[NSDate date] inMode:NSDefaultRunLoopMode dequeue:YES];
[app sendEvent:event];
// Some code is execute here every frame to do some tasks...
usleep(5000);
}
Here's the SurfaceView code:
#interface SurfaceView : NSView
{
Panel* panel;
}
#property (nonatomic) Panel* panel;
- (void)drawRect:(NSRect)dirtyRect;
- (BOOL)isFlipped;
- (void)mouseDown:(NSEvent *)theEvent;
- (void)mouseDragged:(NSEvent *)theEvent;
- (void)mouseUp:(NSEvent *)theEvent;
- (void)keyDown:(NSEvent *)theEvent;
- (BOOL)acceptsFirstResponder;
- (BOOL)becomeFirstResponder;
#end
--
#implementation SurfaceView
#synthesize panel;
- (BOOL)acceptsFirstResponder
{
return YES;
};
- (void)keyDown:(NSEvent *)theEvent
{
// this function is never called
};
...
#end
Here's how I create the view:
NSWindow* window = [[NSWindow alloc] initWithContentRect:NSMakeRect(left, top, wide, tall) styleMask:NSBorderlessWindowMask | NSClosableWindowMask | NSMiniaturizableWindowMask backing:NSBackingStoreBuffered defer:NO];
...
[window makeKeyAndOrderFront:nil];
SurfaceView* mainView = [SurfaceView alloc];
[mainView initWithFrame:NSMakeRect(0, 0, wide, tall)];
mainView.panel = panel;
[window setContentView:mainView];
[window setInitialFirstResponder:mainView];
[window setNextResponder:mainView];
[window makeFirstResponder:mainView];
I found out what was preventing the keyDown event from being called. It was the NSBorderlessWindowMask mask, it prevents the window from become the key and main window. So I have created a subclass of NSWindow called BorderlessWindow:
#interface BorderlessWindow : NSWindow
{
}
#end
#implementation BorderlessWindow
- (BOOL)canBecomeKeyWindow
{
return YES;
}
- (BOOL)canBecomeMainWindow
{
return YES;
}
#end
In addition to answer: Check in your IB checkbox for NSWindow.
Title Bar should be checked. It the similar to NSBorderlessWindowMask
In my case I had to override the keyDown method on both NSView and NSWindow (created a customization of both). In the NSWindow in the keyDown override I had to call
- (void) keyDown:(NSEvent *) event {
if (self.firstResponder != self) {
[self.firstResponder keyDown:event];
}
}
to let the event reach the view. Of course I had also to call makeFirstResponder first on the NSWindow and pass the contentView to it :
[window makeFirstResponder: [window contentView] ];

Forcing UIAlertView into landscape mode

I need to display a UIAlertView in landscape mode. I tried the obvious, setting the transform in the willPresentAlertView: delegate method to no avail:
-(void) willPresentAlertView:(UIAlertView *)alertView {
alertView.transform = CGAffineTransformMakeRotation(M_PI_2);
}
Any suggestions on how to fix this?
Have you tried in the didPresentAlertView?
- (void)didPresentAlertView:(UIAlertView *)alertView
{
// UIAlertView in landscape mode
[UIView beginAnimations:#"" context:nil];
[UIView setAnimationDuration:0.1];
alertView.transform = CGAffineTransformRotate(alertView.transform, 3.14159/2);
[UIView commitAnimations];
}
It should rotate automatically if you're using UIViewController.
Did you forget to return YES for the desired orientations in shouldAutorotateToInterfaceOrientation?
- (BOOL)shouldAutorotateToInterfaceOrientation:(UIInterfaceOrientation)interfaceOrientation
{
return YES; /* auto rotate always */
}
What is the orientation of the view that shows the alert? I had the same problem, I tried to show an UIAlertView inside a landscape view but always appeared in portrait orientation. So, I forced the orientation of the status bar:
[[UIApplication sharedApplication] setStatusBarOrientation:theOrientation];
That worked to me.
Before the Alert Window appeared , Set the current window display at the top. If not do this ,you can see the alert window's rotate animate.
-(void) willPresentAlertView:(UIAlertView *)alertView {
[UIView setAnimationsEnabled:NO];
self.view.window.windowLevel = 2003;
}
Rotate the Alert Window
-(void)didPresentAlertView:(UIAlertView *)alertView
{
UIWindow * alertWindow = alertView.window;
alertWindow.transform = CGAffineTransformMakeRotation(M_PI / 2);
alertWindow.bounds = CGRectMake(0, 0, SCREEN_HEIGHT,SCREEN_WIDTH);
alertWindow.center = CGPointMake(SCREEN_WIDTH / 2, SCREEN_HEIGHT / 2);
[NSTimer scheduledTimerWithTimeInterval:0.1 target:self selector:#selector(showLandscapeAlertView) userInfo:nil repeats:NO];
}
After the Alert window rotated, move the current window back.
-(void)showLandscapeAlertView {
self.view.window.windowLevel = 0;
[UIView setAnimationsEnabled:YES];
}
I was struggling with quite the same issue lately. For me solution was using UIAlertController - cover older handling of UIAlertview and UIActionsheet.
Create new controller, which is dedicated from UIAlertController, where you must overloaded methods viewWillAppear and viewWillDisappear, like in example bellow.
AlertViewController.h
#import <UIKit/UIKit.h>
#interface AlertViewController : UIAlertController
#end
AlertViewController.m
#import "AlertViewController.h"
#interface AlertViewController ()
#end
#implementation AlertViewController
- (void) viewWillAppear:(BOOL)animated {
[self.view setTransform:CGAffineTransformMakeRotation(M_PI_2)];
}
- (void) viewWillDisappear:(BOOL)animated {
[self.view setHidden:YES];
}
...
implement method for showing alert view where you needed.
(void) showInfoAlertView {
AlertViewController *alert = [AlertViewController alertControllerWithTitle:#"My Alert" message:#"This is an alert." preferredStyle:UIAlertControllerStyleAlert];
UIAlertAction* ok = [UIAlertAction actionWithTitle:#"OK" style:UIAlertActionStyleDefault handler:nil];
[alert addAction:ok];
[self presentViewController:alert animated:NO completion:nil];
}

raised tab bar and dismissModalViewControllerAnimated

I created an app with raised Tab Bar like that we see in applications such as Foursquare, Instagram or DailyBooth. To do that I put a UIButton at the center of the Tab Bar and everything seems OK, here the code in my AppDelegate:
- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions {
self.mainButton = [UIButton buttonWithType:UIButtonTypeCustom];
// set up the mainButton with an image and calculate coordinates
[self.mainButton addTarget:self action:#selector(showMainViewController:) forControlEvents:UIControlEventTouchUpInside];
[self.window addSubview:tabBarController.view];
[self.window addSubview:mainButton];
[self.window makeKeyAndVisible];
}
The mainButton has been defined in AppDelegate.h with its property (non atomic, retain) and synthesized.
By pushing the mainButton:
- (IBAction) showMainViewController:(id)sender {
[self.mainButton setHidden:YES];
MainViewController *mainVC = [[MainViewController alloc] initWithNibName:#"MainViewController" bundle:nil];
UINavigationController *nc = [[UINavigationController alloc] initWithRootViewController:mainVC];
[self.mainNavController presentModalViewController:nc animated:YES];
[nc release];
[mainVC release];
}
Since the mainButton remain diplayed I decided to hide it.
At this point I have a doubt: I have to avoid hiding the button?
Problems comes when I decide to dismiss the MainViewController, I don't know if I'm working properly, I'm doing it by a simple [self dismissModalViewControllerAnimated:YES]; in MainViewController.m
The result is that I'm not abled to unhide the mainButton by something like [self.mainButton setHidden:NO];
This helped me to unhide the button.
NSArray *array = self.tabBarController.view.subviews;
for (int i=0; i<[array count]; i++) {
if ([[array objectAtIndex:i] isKindOfClass:[UIButton class]]){
UIButton *uibtn = (UIButton *) [array objectAtIndex:i];
uibtn.hidden = NO;
}
}