When clicking on a Menu item on OSX, the item blinks (on-off-on-close) once before the menu closes.
I was asking my self how can mimic that behavior ? (I've reimplemented a Menu using NSCollectionView, selection & clic on item both work)
I tried 2 thinks that did not work :
mouseOver = false;
[self drawRect:self.bounds];
mouseOver = true;
[self drawRect:self.bounds];
[[self window] performSelector:#selector(orderOut:) withObject:nil afterDelay:0.1];
and
mouseOver = false;
[self setNeedsDisplayInRect:self.bounds];
[self needsDisplay];
mouseOver = true;
[self setNeedsDisplayInRect:self.bounds];
[self needsDisplay];
[[self window] performSelector:#selector(orderOut:) withObject:nil afterDelay:0.1];
I went for that solution :
-(void)mouseDown:(NSEvent *)theEvent {
[super mouseDown:theEvent];
[self performSelector:#selector(blinkItemOnce:) withObject:[NSNumber numberWithBool:NO] afterDelay:0.0];
[self performSelector:#selector(blinkItemOnce:) withObject:[NSNumber numberWithBool:YES] afterDelay:0.05];
[[self window] performSelector:#selector(orderOut:) withObject:nil afterDelay:0.15];
}
-(void) blinkItemOnce:(NSNumber*) b {
mouseOver = [b boolValue];
[self setNeedsDisplayInRect:self.bounds];
[self setNeedsDisplay:YES];
}
Related
- (void)viewDidLoad {
[super viewDidLoad];
[self pingSplash];
UIViewController *next = [[self storyboard] instantiateViewControllerWithIdentifier:#"ViewController"];
[self.navigationController pushViewController:next animated:YES];
}
I mean to finish pinSplash then pushViewController, but it directly goto ViewController page, even without finishing pingSplash, what is a good way to do that kind of job?
For the pingSplash part:
- (void) pingSplash
{
SKSplashIcon *pingSplashIcon = [[SKSplashIcon alloc] initWithImage:[UIImage imageNamed:#"ping.png"] animationType:SKIconAnimationTypePing];
_splashView = [[SKSplashView alloc] initWithSplashIcon:pingSplashIcon backgroundColor:[UIColor grayColor] animationType:SKSplashAnimationTypeBounce];
_splashView.animationDuration = 5.0f;
[self.view addSubview:_splashView];
[_splashView startAnimation];
}
The pingSplash method returns as soon as it starts the animation which is why you end up pushing the view controller too soon.
There are a few ways to solve this. One way would be to pass a block to the pingSplash method that is run after an appropriate delay.
Here's one way:
Update the pingSplash method to take a completion handler block that is run after the 5 second delay.
- (void) pingSplash:(void (^)(void))completion
{
SKSplashIcon *pingSplashIcon = [[SKSplashIcon alloc] initWithImage:[UIImage imageNamed:#"ping.png"] animationType:SKIconAnimationTypePing];
_splashView = [[SKSplashView alloc] initWithSplashIcon:pingSplashIcon backgroundColor:[UIColor grayColor] animationType:SKSplashAnimationTypeBounce];
_splashView.animationDuration = 5.0f;
[self.view addSubview:_splashView];
[_splashView startAnimation];
if (completion) {
dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(_splashView.animationDuration * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{
completion();
});
}
}
Then update your viewDidLoad:
- (void)viewDidLoad {
[super viewDidLoad];
[self pingSplash:^{
UIViewController *next = [[self storyboard] instantiateViewControllerWithIdentifier:#"ViewController"];
[self.navigationController pushViewController:next animated:YES];
}];
}
I am written below code in a button click function.
- (IBAction)btnPlusClicked:(id)sender forEvent:(UIEvent *)event {
//show popover controller
TSActionSheet* actionSheet = [[TSActionSheet alloc] initWithTitle:nil];
[actionSheet addButtonWithTitle:#"Add New" block:^{
//add new button clicked
[self showAddNewView:0];
}];
[actionSheet addButtonWithTitle:#"Copy Yesterday" block:^{
//Copy yesterday clicked
[self showAddNewView:1];
}];
[actionSheet addButtonWithTitle:#"Copy from Date" block:^{
//Copy from date clicked
//show date picker
[self showDatePicker];
}];
actionSheet.cornerRadius = 5;
[actionSheet showWithTouch:event];
}
when above code executes then the -(void)dealloc method of this view controller does not fire on below function of back button touch up inside event.
- (IBAction)btnBackClicked:(id)sender {
[self.navigationController popViewControllerAnimated:animated];
}
-(void)dealloc
{
NSLog(#"-->deallalloc in %#", [self class]);
}
Note that app is ARC compatible.
is there any solution?
To avoid strong reference cycles we need to use weak reference as shown code below.
- (IBAction)btnPlusClicked:(id)sender forEvent:(UIEvent *)event {
if(actionSheet == nil)
{
MyViewController *__weak weakSelf = self;
actionSheet = [[TSActionSheet alloc] initWithTitle:nil];
[actionSheet addButtonWithTitle:#"Add New" block:^{
[weakSelf showAddNewView:0];
}];
[actionSheet addButtonWithTitle:#"Copy Yesterday" block:^{
[weakSelf showAddNewView:1];
}];
[actionSheet addButtonWithTitle:#"Copy from Date" block:^{
[weakSelf showDatePicker];
}];
actionSheet.cornerRadius = 5;
}
[actionSheet showWithTouch:event];
}
I segue from a tableview to detail view controller using custom segues.
#implementation QuickNoteFlipSegue
- (void) perform {
UIViewController *src = (UIViewController *) self.sourceViewController;
UIViewController *dst = (UIViewController *) self.destinationViewController;
dst.navigationItem.hidesBackButton = YES;
[UIView transitionWithView:src.navigationController.view duration:1.00
options:UIViewAnimationOptionTransitionCurlUp
animations:^{
[src.navigationController pushViewController:dst animated:NO];
}
completion:^(BOOL finished){
if (finished) {
}
}];
}
#end
What I would like to do it when the transition has finished, popup the keyboard for my textview.
I currently do this by calling
[textView becomeFirstResponder];
This pops up the keyboard OK but before the transition animation has finished. How to detect when the animation has finished before I popup the keyboard?
I should maybe put something is the completion of the animation, but how to make the custom segue aware of the textview in it's destination?
Do:
-(void)viewDidAppear:(BOOL)animated {
[super viewDidAppear:animated];
[textView becomeFirstResponder];
}
Add a method to your DestinationViewController:
-(void)openKeyboard
{
[textView becomeFirstResponder];
}
Then change your perform method:
- (void) perform {
UIViewController *src = (UIViewController *) self.sourceViewController;
DestinationViewController *dst = (DestinationViewController *) self.destinationViewController;
dst.navigationItem.hidesBackButton = YES;
[UIView transitionWithView:src.navigationController.view duration:1.00
options:UIViewAnimationOptionTransitionCurlUp
animations:^{
[src.navigationController pushViewController:dst animated:NO];
}
completion:^(BOOL finished){
if (finished) {
[dst openKeyboard];
}
}];
}
I have a small project that contains a few UITextFields with a number keypad. When the keyboard is displayed I'm adding a button as a subview for the user to dismiss the keyboard.
However, if the keyboard is active and I close the application, the button I've added will disappear upon relaunching the app. (The app stays inactive, through multitasking, and therefore not quit completely.)
This is the code im using to add the button (my "done" button configured in the xib).
- (void)viewDidLoad
{
[[NSNotificationCenter defaultCenter] addObserver:self
selector:#selector(keyboardWillShow)name:UIKeyboardWillShowNotification object:nil];
[super viewDidLoad];
}
- (void)keyboardWillShow{
[[NSNotificationCenter defaultCenter] removeObserver:self];
// We must perform on delay (schedules on next run loop pass) for the keyboard subviews to be present.
[self performSelector:#selector(addHideKeyboardButtonToKeyboard) withObject:nil afterDelay:0];
}
- (void)addHideKeyboardButtonToKeyboard{
// Locate non-UIWindow.
doneButton.hidden=NO;
UIWindow *keyboardWindow = nil;
for (UIWindow *testWindow in [[UIApplication sharedApplication] windows]) {
if (![[testWindow class] isEqual:[UIWindow class]]) {
keyboardWindow = testWindow;
break;
}
}
if (!keyboardWindow) return;
// Locate UIKeyboard.
UIView *foundKeyboard = nil;
for (UIView __strong *possibleKeyboard in [keyboardWindow subviews]) {
// iOS 4 sticks the UIKeyboard inside a UIPeripheralHostView.
if ([[possibleKeyboard description] hasPrefix:#"<UIPeripheralHostView"]) {
possibleKeyboard = [[possibleKeyboard subviews] objectAtIndex:0];
}
if ([[possibleKeyboard description] hasPrefix:#"<UIKeyboard"]) {
foundKeyboard = possibleKeyboard;
break;
}
}
if (foundKeyboard) {
[doneButton setImage:[UIImage imageNamed:#"doneupHeb.png"] forState:UIControlStateNormal];
[doneButton setImage:[UIImage imageNamed:#"donedownHeb.png"] forState:UIControlStateHighlighted];
doneButton.frame = CGRectMake(-1, 163, 106, 53);
[foundKeyboard addSubview:doneButton];
// Add the button to foundKeyboard.
}
}
-(void)textFieldDidEndEditing:(UITextField *)textField{
[loan resignFirstResponder];
[YearCycle resignFirstResponder];
[prime resignFirstResponder];
[MothlyReturn resignFirstResponder];
[doneButton removeFromSuperview];
doneButton = nil;
}
- (void)textFieldDidBeginEditing:(UITextField *)textField{
textField.delegate=self;
//editingField = textField;
if ([prime isFirstResponder]||[MothlyReturn isFirstResponder]){
scroll.contentOffset = CGPointMake(0, 166 );
}
// if ([YearCycle isFirstResponder]){
// scroll.contentOffset = CGPointMake(0, 200);
}
- (IBAction)closeNumpad:(id)sender{
[loan resignFirstResponder];
[YearCycle resignFirstResponder];
[prime resignFirstResponder];
[MothlyReturn resignFirstResponder];
scroll.contentOffset = CGPointMake(0, 0);
doneButton.hidden=YES;
}
i fixed the problem with a little help from other questions in the website - for all of you that have or will have the problem - this is the code:
Please note: the button himself are designed in the xib file and not in the code.
the .h file:
BOOL firstTime;
BOOL add;
BOOL keyboardOpened;
IBOutlet UIButton *doneButton;
the .m file:
- (void)viewDidLoad
{
[super viewDidLoad];
firstTime = TRUE;
add = TRUE;
[[NSNotificationCenter defaultCenter] addObserver:self selector:#selector(keyboardDidShow:) name:UIKeyboardDidShowNotification object:nil]; // Do any additional setup after loading the view, typically from a nib.
}
- (void)viewDidUnload
{
// [[NSNotificationCenter defaultCenter] removeObserver:self];
[super viewDidUnload];
// Release any retained subviews of the main view.
// e.g. self.myOutlet = nil;
}
- (BOOL)shouldAutorotateToInterfaceOrientation:(UIInterfaceOrientation)interfaceOrientation
{
// Return YES for supported orientations
return (interfaceOrientation != UIInterfaceOrientationPortraitUpsideDown);
}
- (void)addButtonToKeyboard {
// create custom button
/* UIButton *doneButton = [UIButton buttonWithType:UIButtonTypeCustom];
doneButton.frame = CGRectMake(0, 163, 106, 53);
doneButton.adjustsImageWhenHighlighted = NO;
[doneButton setImage:[UIImage imageNamed:#"DoneUp.png"] forState:UIControlStateNormal];
[doneButton setImage:[UIImage imageNamed:#"DoneDown.png"] forState:UIControlStateHighlighted];
doneButton.tag = 3;
[doneButton addTarget:self action:#selector(doneButton:) forControlEvents:UIControlEventTouchUpInside];*/
// locate keyboard view
UIWindow* tempWindow = [[[UIApplication sharedApplication] windows] objectAtIndex:1];
UIView* keyboard;
for(int i=0; i<[tempWindow.subviews count]; i++) {
keyboard = [tempWindow.subviews objectAtIndex:i];
// keyboard found, add the button
if ([[keyboard description] hasPrefix:#"<UIPeripheralHostView"] == YES && add){
doneButton.frame = CGRectMake(-1, 163, 106, 53);
[keyboard addSubview:doneButton];
}
}
}
- (void)removeButtonFromKeyboard
{
// locate keyboard view
UIWindow* tempWindow = [[[UIApplication sharedApplication] windows] objectAtIndex:1];
UIView* keyboard;
for(int i=0; i<[tempWindow.subviews count]; i++) {
keyboard = [tempWindow.subviews objectAtIndex:i];
// keyboard found, remove the button
if([[keyboard description] hasPrefix:#"<UIPeripheralHost"] == YES) [[keyboard viewWithTag:3] removeFromSuperview];
}
}
- (IBAction)doneButton:(id)sender {
[loan resignFirstResponder];
[YearCycle resignFirstResponder];
[ageOfCeo resignFirstResponder];
[YearofBusiness resignFirstResponder];
scroll.contentOffset = CGPointMake(0, 0);
if (![[[UIDevice currentDevice] model] isEqualToString:#"iPad"] && ![[[UIDevice currentDevice] model] isEqualToString:#"iPad Simulator"])
{
[self removeButtonFromKeyboard];
firstTime = TRUE;
}
}
- (BOOL)textFieldShouldReturn:(UITextField *)theTextField {
[theTextField resignFirstResponder];
return YES;
}
- (void)textFieldDidBeginEditing:(UITextField *)textField
{
if ([ageOfCeo isFirstResponder]||[YearofBusiness isFirstResponder]){
scroll.contentOffset = CGPointMake(0, 166 );
}
// firstResponder = textField;
}
- (void)keyboardDidShow:(id)sender
{
if (![[[UIDevice currentDevice] model] isEqualToString:#"iPad"] && ![[[UIDevice currentDevice] model] isEqualToString:#"iPad Simulator"])
{
NSLog(#"%#",[[UIDevice currentDevice] model]);
[self addButtonToKeyboard];
keyboardOpened = TRUE;
}
}
- (void)textFieldDidEndEditing:(UITextField *)textField
{
if (![[[UIDevice currentDevice] model] isEqualToString:#"iPad"] && ![[[UIDevice currentDevice] model] isEqualToString:#"iPad Simulator"])
{
[self removeButtonFromKeyboard];
keyboardOpened = FALSE;
}
}
i think your problem because you are removing the observer in the - (void)keyboardWillShow .. try to put this line [[NSNotificationCenter defaultCenter] removeObserver:self]; in the -(void)viewDidUnload
I'm working on a status bar app that has a left and right click. I've got the start of this working by following the tips from other posts but I'm not sure how to go about showing a menu on right click.
I use a subclassed NSView as the custom view of my NSStatusItem and have the right and left clicks executing different functions:
- (void)mouseDown:(NSEvent *)theEvent{
[super mouseDown:theEvent];
if ([theEvent modifierFlags] & NSCommandKeyMask){
[self.target performSelectorOnMainThread:self.rightAction withObject:nil waitUntilDone:NO];
}else{
[self.target performSelectorOnMainThread:self.action withObject:nil waitUntilDone:NO];
}
}
- (void)rightMouseDown:(NSEvent *)theEvent{
[super rightMouseDown:theEvent];
[self.target performSelectorOnMainThread:self.rightAction withObject:nil waitUntilDone:NO];
}
How can I show a menu on right click, the same way the standard NSStatusItem does on left click?
NSStatusItem popUpStatusItemMenu: did the trick. I am calling it from my right click action and passing in the menu I want to show and it's showing it! This is not what I would have expected this function to do, but it's working.
Here's the important parts of what my code looks like:
- (void)showMenu{
// check if we are showing the highlighted state of the custom status item view
if(self.statusItemView.clicked){
// show the right click menu
[self.statusItem popUpStatusItemMenu:self.rightClickMenu];
}
}
// menu delegate method to unhighlight the custom status bar item view
- (void)menuDidClose:(NSMenu *)menu{
[self.statusItemView setHighlightState:NO];
}
- (void)applicationDidFinishLaunching:(NSNotification *)aNotification{
// setup custom view that implements mouseDown: and rightMouseDown:
self.statusItemView = [[ISStatusItemView alloc] init];
self.statusItemView.image = [NSImage imageNamed:#"menu.png"];
self.statusItemView.alternateImage = [NSImage imageNamed:#"menu_alt.png"];
self.statusItemView.target = self;
self.statusItemView.action = #selector(mainAction);
self.statusItemView.rightAction = #selector(showMenu);
// set menu delegate
[self.rightClickMenu setDelegate:self];
// use the custom view in the status bar item
self.statusItem = [[NSStatusBar systemStatusBar] statusItemWithLength:NSSquareStatusItemLength];
[self.statusItem setView:self.statusItemView];
}
Here is the implementation for the custom view:
#implementation ISStatusItemView
#synthesize image = _image;
#synthesize alternateImage = _alternateImage;
#synthesize clicked = _clicked;
#synthesize action = _action;
#synthesize rightAction = _rightAction;
#synthesize target = _target;
- (void)setHighlightState:(BOOL)state{
if(self.clicked != state){
self.clicked = state;
[self setNeedsDisplay:YES];
}
}
- (void)drawImage:(NSImage *)aImage centeredInRect:(NSRect)aRect{
NSRect imageRect = NSMakeRect((CGFloat)round(aRect.size.width*0.5f-aImage.size.width*0.5f),
(CGFloat)round(aRect.size.height*0.5f-aImage.size.height*0.5f),
aImage.size.width,
aImage.size.height);
[aImage drawInRect:imageRect fromRect:NSZeroRect operation:NSCompositeSourceOver fraction:1.0f];
}
- (void)drawRect:(NSRect)rect{
if(self.clicked){
[[NSColor selectedMenuItemColor] set];
NSRectFill(rect);
if(self.alternateImage){
[self drawImage:self.alternateImage centeredInRect:rect];
}else if(self.image){
[self drawImage:self.image centeredInRect:rect];
}
}else if(self.image){
[self drawImage:self.image centeredInRect:rect];
}
}
- (void)mouseDown:(NSEvent *)theEvent{
[super mouseDown:theEvent];
[self setHighlightState:!self.clicked];
if ([theEvent modifierFlags] & NSCommandKeyMask){
[self.target performSelectorOnMainThread:self.rightAction withObject:nil waitUntilDone:NO];
}else{
[self.target performSelectorOnMainThread:self.action withObject:nil waitUntilDone:NO];
}
}
- (void)rightMouseDown:(NSEvent *)theEvent{
[super rightMouseDown:theEvent];
[self setHighlightState:!self.clicked];
[self.target performSelectorOnMainThread:self.rightAction withObject:nil waitUntilDone:NO];
}
- (void)dealloc{
self.target = nil;
self.action = nil;
self.rightAction = nil;
[super dealloc];
}
#end
One option is to just fake the left mouse down:
- (void)rightMouseDown: (NSEvent *)event {
NSEvent * newEvent;
newEvent = [NSEvent mouseEventWithType:NSLeftMouseDown
location:[event locationInWindow]
modifierFlags:[event modifierFlags]
timestamp:CFAbsoluteTimeGetCurrent()
windowNumber:[event windowNumber]
context:[event context]
eventNumber:[event eventNumber]
clickCount:[event clickCount]
pressure:[event pressure]];
[self mouseDown:newEvent];
}
Added little something for when you need title in your view
- (void)drawRect:(NSRect)rect{
if(self.clicked){
[[NSColor selectedMenuItemColor] set];
NSRectFill(rect);
if(self.alternateImage){
[self drawImage:self.alternateImage centeredInRect:rect];
}else if(self.image){
[self drawImage:self.image centeredInRect:rect];
} else {
[self drawTitleInRect:rect];
}
} else if(self.image){
[self drawImage:self.image centeredInRect:rect];
} else {
[self drawTitleInRect:rect];
}
}
-(void)drawTitleInRect:(CGRect)rect
{
CGSize size = [_title sizeWithAttributes:nil];
CGRect newRect = CGRectMake(MAX((rect.size.width - size.width)/2.f,0.f),
MAX((rect.size.height - size.height)/2.f,0.f),
size.width,
size.height);
NSDictionary *attributes = #{NSForegroundColorAttributeName : self.clicked?[NSColor highlightColor]:[NSColor textColor]
};
[_title drawInRect:newRect withAttributes:attributes];
}
- (void)statusItemAction {
NSEvent *event = NSApp.currentEvent;
if (event.type == NSEventTypeRightMouseDown || (event.modifierFlags & NSEventModifierFlagControl)) {
[self toggleMenu];
} else {
[self togglePopOver];
}
}