Why is controlTextDidEndEditing being called when I send setEditable? - objective-c

//1:
-(void) TextFieldEdit:(CDTextField *)textField{
[textField setEditable:YES];
}
//2:
- (void)controlTextDidEndEditing:(NSNotification *)aNotification{
NSTextField* textField = (NSTextField *)[aNotification object];
[textField setEditable:NO];
}
I hope setEditable at time 1, and close it at time 2.
But I found when I sent setEditable Xcode immediately called the controlTextDidEndEditing:.
Why?
EDIT: The first method is invoked via the following sub-classed method:
-(void)mouseDown:(NSEvent *)event {
if ([event type]==1)
{
NSInteger key=[event modifierFlags];
if ( key & NSCommandKeyMask)
{[self.delegate CDTextFieldEdit:self]; }
else
if (event.clickCount >1)
{[self.delegate CDTextFieldClicked:self]; return;}
}
[super mouseDown:event];
}

Related

Cocoa - NSCursor resets to the default cursor when a key is pressed

I'm working on an application that has a window with a fully sized NSOpenGLView. I'm using [view addCursorRect] and [cursor set] to show a custom cursor, but when I press any key on the keyboard, the cursor resets to the default arrow. I've also tried overriding resetCursorRects and calling invalidateCursorRects when a key is pressed, which results in a flickering cursor.
The cursor switches back to my custom cursor when I click anywhere in the view, so I suppose the keyboard presses somehow unfocus my view. Is there any way to prevent the view from becoming unfocused when I press a key?
You need to remember your cursor settings + when you need to reset call e.g refreshCursor method. Example implementation:
- (void)refreshCursor
{
NSCursor *cursor = nil;
if ([self graphic]) {
cursor = [graphic hoverCursorForPoint:[self mousePosition]];
} else if ([self group]){
cursor = [NSCursor openHandCursor];
}
[self setHoverCursor:cursor];
[self refresh];
}
- (void)updateTrackingAreas
{
[super updateTrackingAreas];
[self removeTrackingArea:[self trackingArea]];
NSTrackingArea *area = [[NSTrackingArea alloc] initWithRect:[self visibleRect]
options:NSTrackingActiveAlways|NSTrackingMouseEnteredAndExited|NSTrackingMouseMoved
owner:self userInfo:nil];
[self setTrackingArea:area];
[self addTrackingArea:[self trackingArea]];
}
- (void)setCursorRects
{
[self discardCursorRects];
if ([self hoverCursor]) {
[self addCursorRect:[self visibleRect] cursor:[self hoverCursor]];
}
}
- (void)setHoverCursor:(NSCursor *)hoverCursor {
if (_hoverCursor != hoverCursor) {
_hoverCursor = hoverCursor;
[self setCursorRects];
}
}
- (void)resetCursorRects {
[super resetCursorRects];
[self setCursorRects];
}
- (void)mouseExited:(NSEvent *)event
{
[[NSCursor currentSystemCursor] set];
}
- (void)mouseEntered:(NSEvent *)event
{
}
- (void)mouseMoved:(NSEvent *)event
{
[self refreshCursor];
}
- (void)keyUp:(NSEvent *)event
{
[self refreshCursor];
}
//allow key events
- (BOOL)acceptsFirstResponder
{
return YES;
}

Maintaining reference to UITextField "text" field

Problem:
I am trying to create a custom UITextField class "UIValidatedTextField" which allows one to set certain rules as to whether input is valid. For example, you can set a regex parameter to ensure that input is of a specific format, i.e. a password, email address, etc...
Another ability of this is to specify and set a parameter that references another UITextField and ensures that the input matches the input from that other UITextField.
The issue I am having here, is that I am setting this reference to another UITextField. However, when I access its "text" field I find that there is nothing in the text field even when I type something into it.
I have provided related code below:
#import "UIRegisterViewController.h"
#import "UIRegisterViewCell.h"
#import "UIValidatedTextField.h"
#import "NSConstants.h"
#interface UIRegisterViewController ()
#end
#implementation UIRegisterViewController
- (void)viewDidLoad {
[super viewDidLoad];
_tableView.delegate = self;
_tableView.dataSource = self;
_tableItems = #[#"name", #"email", #"netId", #"username", #"password", #"confirmPassword"];
}
- (void)didReceiveMemoryWarning {
[super didReceiveMemoryWarning];
// Dispose of any resources that can be recreated.
}
#pragma mark - Table view data source
- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section {
return [_tableItems count];
}
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath{
NSString *cellIdentifier = [_tableItems objectAtIndex:indexPath.row];
UIRegisterViewCell *cell = [_tableView dequeueReusableCellWithIdentifier:cellIdentifier forIndexPath:indexPath];
if ([cellIdentifier isEqualToString:#"email"]) {
[cell.textField setRegex:VALID_DUKE_EMAIL_REGEX];
} else if ([cellIdentifier isEqualToString:#"netId"]) {
//Validation?
} else if ([cellIdentifier isEqualToString:#"username"]) {
//Validation?
//THIS IS THE CELL THAT I WANT TO COMPARE INPUT TO
} else if ([cellIdentifier isEqualToString:#"password"]) {
[cell.textField setRegex:VALID_PASSWORD_REGEX];
//SETTING THE TEXT FIELD IN QUESTION HERE...
} else if ([cellIdentifier isEqualToString:#"confirmPassword"]) {
[cell.textField setRegex:VALID_PASSWORD_REGEX];
NSIndexPath *index = [NSIndexPath indexPathForRow:4 inSection:0];
UIRegisterViewCell *confirm =(UIRegisterViewCell *)[self tableView:_tableView cellForRowAtIndexPath:index];
[cell.textField setConfirm:confirm.textField];
}
cell.textField.delegate = self;
return cell;
}
#pragma mark - Text Field Delegate
- (BOOL)textFieldShouldReturn:(UITextField *)textField {
[textField resignFirstResponder];
return YES;
}
#end
Note that the textFields are UIValidatedTextFields - a custom class provided below:
#import "UIValidatedTextField.h"
#import "NSArgumentValidator.h"
#implementation UIValidatedTextField
- (id) initWithCoder:(NSCoder *)aDecoder {
self = [super initWithCoder:aDecoder];
if (self) {
[self initialize];
}
return self;
}
- (id)initialize {
if (self) {
[[NSNotificationCenter defaultCenter] addObserver:self selector:#selector(textFieldDidChange:)
name:UITextFieldTextDidChangeNotification object:self];
[self validate]; //Validate in case editing began before observer was set.
}
return self;
}
- (void) setOptional:(BOOL)isOptional {
_isOptional = isOptional;
}
- (BOOL) isOptional {
return _isOptional;
}
- (void) setRegex:(NSString *)regex {
_regex = regex;
}
//SET THE TEXT FIELD TO COMPARE INPUT AGAINST HERE.
- (void) setConfirm:(UITextField *)confirm {
_confirm = confirm;
[[NSNotificationCenter defaultCenter] addObserver:self selector:#selector(textFieldDidChange:)
name:UITextFieldTextDidChangeNotification object:_confirm];
[self validate]; //Validate in case editing on confirm began before observer was set.
}
- (void) setQuery:(NSString *)query {
_query = query;
}
- (void) textFieldDidChange:(NSNotification *)notification {
NSLog(#"UPDATE");
_isValid = [self validate];
[self showInputValidation];
}
- (BOOL) validateRegex {
if (_regex.length == 0) {
return true;
}
return [NSArgumentValidator isValid:self.text withRegex:_regex];
}
- (BOOL) validateConfirm {
// NSLog(#"%# : %#", [_confirm text], self.text);
if (_confirm == NULL) {
//NSLog(#"IS NULL");
return true;
}
return [self.text isEqualToString:_confirm.text];
}
- (BOOL) validateQuery {
return true;
}
- (BOOL) validate {
_isValid = (self.text == 0 && _isOptional) || ((self.text != 0) && [self validateRegex] && [self validateConfirm] && [self validateQuery]);
return _isValid;
}
//IF ANYONE HAS A SOLUTION AS TO HOW TO MAKE CHANGING BORDER COLOR CHANGE THE COLOR ALONG THE ROUNDED BORDER THAT IS PRESENT AS OPPOSED TO A RECTANGULAR BORDER LET ME KNOW.
- (void) showInputValidation {
self.layer.borderWidth = 1.0;
if (self.text.length == 0) {
self.layer.borderColor = [[UIColor blackColor] CGColor];
} else if (_isValid) {
self.layer.borderColor = [[UIColor greenColor] CGColor];
} else {
self.layer.borderColor = [[UIColor redColor] CGColor];
}
}
- (void) finalize {
[super finalize];
[[NSNotificationCenter defaultCenter] removeObserver:self
name:UITextFieldTextDidChangeNotification object:self];
if (_confirm != NULL) {
[[NSNotificationCenter defaultCenter] removeObserver:self
name:UITextFieldTextDidChangeNotification object:_confirm];
}
}
#end
Thanks for the help!
One glaring bug in this code is that you're setting the regex in cellForRowAtIndexPath:, even though all cell are reusing the same cell object. cellForRowAtIndexPath: should be used only to set cell content, like text and color. Instead, create an IBOutlet to the validating text fields and add their regexes in viewDidLoad. Better yet, scrap the custom subclass entirely and instead run your regex validation whenever one of the relevant text fields fires off an event when editing is finished.

UIImagePIckerController appears but the camera does not start

Yes, It might be a duplicate question of this. But since it didn't get an answer, I will be more specific on the case and code:
I have 3 involved UIViewControllers:
WelcomeView - the first one
TakePhotoViewController - the second one who is delegate of the OverlayviewController
OverlayViewController - custom view for the camera.
Scenario:
User enter WelcomeView and clicks on a button to be transfered with segue to TakeView.
UIImageViewController is being opened to take a photo.
The user clicks on cancel button - didCancelCamera method in TakePhotoViewController is being invoked and he returns to WelcomeView
The user leaves the app.
The user re-opens the app and perform step 1 again.
THE IMAGE PICKER IS NOT BEING OPENED. I COULD TAKE A PHOTO AND IT'S OK - BUT THE USER CAN'T SEE WHAT HE IS TAKING.
OverlayViewController.h
#interface OverlayViewController : BaseViewController<UIImagePickerControllerDelegate,UINavigationControllerDelegate>
#property (nonatomic,weak) id<OverlayViewControllerDelegate> delegate;
#property (nonatomic,retain) UIImagePickerController *imagePickerController;
#end
OverlayViewController.m:
- (id)initWithCoder:(NSCoder *)aDecoder
{
self = [super initWithCoder:aDecoder];
if (self) {
self.imagePickerController = [[UIImagePickerController alloc] init];
self.imagePickerController.delegate = self;
}
return self;
}
- (void)viewDidLoad
{
[super viewDidLoad];
self.view.backgroundColor = [UIColor clearColor];
}
- (IBAction)takePicture:(id)sender {
[self.imagePickerController takePicture];
}
- (IBAction)cancelImagePicker:(id)sender {
[self.delegate didCancelCamera];
}
- (void) setupImagePicker:(UIImagePickerControllerSourceType) sourceType
{
self.imagePickerController.sourceType = sourceType;
if (sourceType == UIImagePickerControllerSourceTypeCamera)
{
self.imagePickerController.showsCameraControls = NO;
if ([[self.imagePickerController.cameraOverlayView subviews] count] ==0)
{
CGRect overlayViewFrame = self.imagePickerController.cameraOverlayView.frame;
CGRect newFrame = CGRectMake(0.0, CGRectGetHeight(overlayViewFrame)-self.view.frame.size.height-10.0, CGRectGetWidth(overlayViewFrame), self.view.frame.size.height + 10.0);
self.view.frame = newFrame;
[self.imagePickerController.cameraOverlayView addSubview:self.view];
}
}
}
- (void)finishAndUpdate
{
[NSThread detachNewThreadSelector:#selector(threadStartAnimating:) toTarget:self withObject:nil];
[self.delegate didFinishWithCamera]; // tell our delegate we are done with the camera
}
- (void)imagePickerController:(UIImagePickerController *)picker didFinishPickingMediaWithInfo:(NSDictionary *)info
{
[self finishAndUpdate];
}
TakePhotoViewController.h
#interface TakePhotoViewController : BaseViewController<UIImagePickerControllerDelegate,UINavigationControllerDelegate,OverlayViewControllerDelegate>
#property (nonatomic, retain) OverlayViewController *overlayViewController;
#end
TakePhotoViewController.m
- (void)viewDidLoad
{
[super viewDidLoad];
UIStoryboard *sb = [UIStoryboard storyboardWithName:#"MainStoryboard" bundle:nil];
// Insert the overlay
self.overlayViewController = (OverlayViewController *)[sb instantiateViewControllerWithIdentifier:#"Overlay"];
self.overlayViewController.delegate = self;
}
- (void)viewDidUnload
{
self.overlayViewController = nil;
[super viewDidUnload];
// Release any retained subviews of the main view.
}
- (void)openImagePicker {
if([UIImagePickerController isSourceTypeAvailable:UIImagePickerControllerSourceTypeCamera]){
[self showImagePicker:UIImagePickerControllerSourceTypeCamera];
}
else{
[self showImagePicker:UIImagePickerControllerSourceTypePhotoLibrary];
}
}
- (void)viewDidAppear:(BOOL)animated{
if (appDelegate.shouldOpenPicker){
[self openImagePicker];
}
}
- (void)showImagePicker:(UIImagePickerControllerSourceType)sourceType
{
if ([UIImagePickerController isSourceTypeAvailable:sourceType])
{
[self.overlayViewController setupImagePicker:sourceType];
[self presentViewController:self.overlayViewController.imagePickerController animated:YES completion:nil];
}
}
-(void)didCancelCamera{
[[self.overlayViewController.imagePickerController presentingViewController] dismissViewControllerAnimated:NO completion:^ {
[self performSegueWithIdentifier:#"fromTakeToWelcome" sender:self];
}];
}
I found the bug.
The method
-(void)didCancelCamera from TakePhotoViewController is being called when the user clicks on - (IBAction)cancelImagePicker:(id)sender in OverlayViewController.
However, somehow the code in didCancelCamera causes viewDidAppear method of TakePhotoViewController to be invoked again and reopen the image picker.
I have no idea why
[[self.overlayViewController.imagePickerController presentingViewController] dismissViewControllerAnimated:NO completion:^ {
[self performSegueWithIdentifier:#"fromTakeToWelcome" sender:self];
}];
causes the viewDidAppear method of that view (TakePhoto) being recalled again.
Hope that it will help someone

NSStatusItem right click menu

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];
}
}

UITextField in a UIActionSheet only calling some delegate methods

The below code shows that when a user does a long press gesture on a Table View Cell, then a UIActionSheet launches with a UITextField inside of it. When tapping the UITextField, the keyboard launches, and textFieldShouldBeginEditing and textFieldDidBeginEditing get called, but the text field won't accept the key taps.
Hitting the return key won't trigger the delegate methods, but tapping one of the UIActionSheet buttons will trigger textFieldShouldEndEditing and then textFieldDidEndEditing.
I'm setting the textField to become the first responder, so I'm not sure why it's not accepting input from the keyboard. Any suggestions?
- (void)longPress:(UILongPressGestureRecognizer *)gesture
{
// only when gesture was recognized, not when ended
if (gesture.state == UIGestureRecognizerStateBegan)
{
// get affected cell
SinTableViewCell *cell = (SinTableViewCell *)[gesture view];
// get indexPath of cell
NSIndexPath *indexPath = [self.tableView indexPathForCell:cell];
// do something with this action
NSLog(#"Long-pressed cell at row %d", indexPath);
AppDelegate_Shared *appDelegate = (AppDelegate_Shared*)[UIApplication sharedApplication].delegate;
//setup UITextField for the UIActionSheet
UITextField *textField = [[UITextField alloc] initWithFrame:CGRectMake(0, 170, 320, 200)];
textField.borderStyle = UITextBorderStyleBezel;
textField.backgroundColor = UIColorFromRGB(0XFFFFFF);
textField.text = #"";
textField.delegate = self;
[textField setKeyboardType:UIKeyboardTypeAlphabet];
[textField setKeyboardAppearance:UIKeyboardAppearanceAlert];
//setup UIActionSheet
UIActionSheet *asheet = [[UIActionSheet alloc] initWithTitle:#"Add Notes"
delegate:self
cancelButtonTitle:#"Cancel"
destructiveButtonTitle:nil
otherButtonTitles: #"Save", nil];
[asheet showFromTabBar:appDelegate.tabBarController.tabBar];
[asheet setFrame:CGRectMake(0, 100, 320,380)];
[asheet insertSubview:textField atIndex:0];
//[textField becomeFirstResponder];
//memory management
[textField release];
[asheet release];
}
}
#pragma mark -
#pragma mark UIActionSheetDelegate
- (void)actionSheet:(UIActionSheet *)actionSheet willDismissWithButtonIndex:(NSInteger)buttonIndex {
}
- (void)actionSheet:(UIActionSheet *)actionSheet didDismissWithButtonIndex:(NSInteger)buttonIndex {
}
#pragma mark -
#pragma mark UITextFieldDelegate
- (BOOL)textFieldShouldBeginEditing:(UITextField *)textField {
NSLog(#"textFieldShouldBeginEditing");
return YES;
}
- (void)textFieldDidBeginEditing:(UITextField *)textField {
NSLog(#"textFieldDidBeginEditing");
[textField becomeFirstResponder];
}
- (BOOL)textFieldShouldEndEditing:(UITextField *)textField {
NSLog(#"textFieldShouldEndEditing");
return YES;
}
//should save the notes value here, I think
- (void)textFieldDidEndEditing:(UITextField *)textField {
NSLog(#"textFieldDidEndEditing");
[textField resignFirstResponder];
}
- (BOOL)textField:(UITextField *)textField shouldChangeCharactersInRange:(NSRange)range replacementString:(NSString *)string{
return YES;
}
- (BOOL)textFieldShouldClear:(UITextField *)textField {
NSLog(#"textFieldShouldClearEditing");
return YES;
}
- (BOOL)textFieldShouldReturn:(UITextField *)textField {
NSLog(#"in textFieldShouldReturn");
return YES;
}
Your question is a little bit old but not marked as answered, so if it is helpful for you or other viewers I post my solution from my own SO question. I started up with the same problem as you had and ended up in the fact that UIActionSheet really eats up some important events which are necessary to get the keyboard working properly.
My linked posted code unfortunately works only in portrait orientation, anyway it does it.
Is UITextFieldDelegate and UIActionSheetDelegate in your header?
If not, there could be problems while setting the textfield.delegate to self