Call a method from within a sibling method with parameters - objective-c

I have followed the first steps tutorial from apple, and I have a working app that can read text and display it in a label on button click. When I hit enter the textfield is resigned and the keyboard disappears, and when I hit the button the changeGreeting method is invoked. I want to call the changeGreeting function in the function thats used when I hit enter which is textFieldShouldReturn.
I tried everything I could think of, and read a lot online but Im not sure how to deal with (id)sender as a param for example. How should I edit my code to call changeGreeting on textfield enter?
code below:
- (IBAction)changeGreeting:(id)sender {
self.userName = self.textField.text;
NSString *nameString = self.userName;
NSString *endString = #"burp";
int r = arc4random() % 74;
if ([nameString length] == 0) {
nameString = #"World";
}
NSString *greeting = [[NSString alloc] initWithFormat:#"Hello, %#, %#! Random String of numbers: %d", nameString, endString, r];
self.label.text = greeting;
}
- (BOOL)textFieldShouldReturn:(UITextField *)theTextField {
if (theTextField == self.textField) {
[theTextField resignFirstResponder];
// on enter the keyboard is removed, but I want the
// changeGreeting method involed too, something like
// [self changeGreeting]
}
return YES;
}
thanks in adv

You already have it. Just ignore the sender parameter:
- (BOOL)textFieldShouldReturn:(UITextField *)theTextField {
if (theTextField == self.textField) {
[theTextField resignFirstResponder];
// on enter the keyboard is removed, but I want the
// changeGreeting method involed too, something like
[self changeGreeting:nil]
}

Related

How do I switch views after signing up and logging in?

So the backend is working, I can successfully create accounts and login. Now I want them to transition to the home screen so how I do I set so if the sigup/login is successful it transitions? This is my code:
- (IBAction)signUp:(UIButton *)sender {
NSString *name = _textUsername.text;
NSString *password = _textPassword.text;
NSString *email = [_textEmail.text lowercaseString];
//---------------------------------------------------------------------------------------------------------------------------------------------
if ([name length] == 0) { [ProgressHUD showError:#"Name must be set."]; return; }
if ([password length] == 0) { [ProgressHUD showError:#"Password must be set."]; return; }
if ([email length] == 0) { [ProgressHUD showError:#"Email must be set."]; return; }
//---------------------------------------------------------------------------------------------------------------------------------------------
[ProgressHUD show:#"Please wait..." Interaction:NO];
PFUser *user = [PFUser user];
user.username = name;
user.password = password;
user.email = email;
user[PF_USER_EMAILCOPY] = email;
user[PF_USER_FULLNAME] = name;
user[PF_USER_FULLNAME_LOWER] = [name lowercaseString];
[user signUpInBackgroundWithBlock:^(BOOL succeeded, NSError *error)
{
if (error == nil)
{
ParsePushUserAssign();
[ProgressHUD showSuccess:#"Succeed."];
[self dismissViewControllerAnimated:YES completion:nil];
}
else [ProgressHUD showError:error.userInfo[#"error"]];
}];
Also any idea why my text field doesn't automatically go to the next one? Pretty sure the code is right. Thanks.
- (BOOL)textFieldShouldReturn:(UITextField *)textField
{
if (textField == _textUsername)
{
[textField resignFirstResponder];
[_textEmail becomeFirstResponder];
}
else if (textField == _textEmail)
{
[textField resignFirstResponder];
[_textPassword becomeFirstResponder];
}
else if (textField == _textPassword)
{
[textField becomeFirstResponder];
[_textBirthday becomeFirstResponder];
}
else if (textField == _textBirthday)
{
[self actionRegister];
}
return YES;
The way I arrange my parse.com apps is to have the main VC, the one that does the app's usual business for a logged-in user, be the storyboard's starting VC. It's first job in viewDidAppear, is to check that the PFUser currentUser exists (and is logged-in if your app requires it). If it isn't, then and only then, present the login/signup VC.
When login/signup dismisses as you have it in the posted code, viewWillAppear in your main VC fires again and detects the ready-to-run condition. It's action in that case is to carry on with initialization for a logged in user.
EDIT - For example, I have an app where the main VC is the starting VC in storyboard. It has a segue with identifier == #"GetUser" drawn from it to my equivalent of your signup/login VC.
Here's the (slightly simplified) viewDidAppear from my main VC.
- (void)viewDidAppear:(BOOL)animated {
[super viewDidAppear:animated];
// I require only a non-nil currentUser to run the app. Your check
// might be more elaborate...
PFUser *user = [PFUser currentUser];
if (user) {
// put up a "busy" UI. In my app, it's just a view with UIActivityIndicator
// update the user so we know its current
[user fetchInBackgroundWithBlock:^(PFObject *object, NSError *error) {
// remove the "busy" UI
[self startTheApp];
}];
} else {
[self performSegueWithIdentifier:#"GetUser" sender:self];
}
}
-(void)startTheApp {
// fetch data from parse.com and reload the table view that's the
// centerpiece of the UI for this VC.
}
The nice behavior here is when the login/signup succeeds, the currentUser is non-nil, so when the login/signup VC is dismissed, viewDidAppear as shown above is fired again because the main VC underneath is being revealed again. And on that second time through, the currentUser passes the starting condition and we go to startTheApp.

SplitViewController with a Login View Controller as the root

So I've been researching on how to make a login view controller as the initial view controller instead of the splitview.
Some of the answers I've seen would recommend a modal view to be loaded? I'm not sure how that is set up.
eg.
How to add a login view before a UISplitViewController iPad
and
How to implement SplitViewController on second level.?
So do I add those on the loginviewcontroller class? Or where?
Any advice is welcome.
Thanks!!
I've done this by creating two storyboards: one with the (full-screen) login and one with the split-view.
To switch between them, I've added a custom protocol:
#import <Foundation/Foundation.h>
#protocol RootViewControllerDelegate <NSObject>
-(void)switchToStoryboard: (UIStoryboard *) storyboad animationDirectionOrNil: (NSString *)direction;
#end
The AppDelegate then implements this protocol:
-(void)switchToStoryboard:(id)storyboad animationDirectionOrNil:(NSString *)direction {
UIViewController *newRoot=[storyboad instantiateInitialViewController];
if ([newRoot respondsToSelector:#selector(setRootViewControllerDelegate:)]) {
[newRoot setRootViewControllerDelegate:self];
}
self.window.rootViewController=newRoot;
if(direction){
CATransition* transition=[CATransition animation];
transition.type=kCATransitionPush;
transition.subtype=direction;
[self.window.layer addAnimation:transition forKey:#"push_transition"];
}
}
As you can see, it tries to set itself as the delegate again, so the other view-controller can switch back or to another storyboard. In order for this to work, you would have to subclass UISplitView:
Header
#import <UIKit/UIKit.h>
#import "RootViewControllerDelegate.h"
#interface MySplitViewController : UISplitViewController
#property (nonatomic, weak) id <RootViewControllerDelegate> rootViewControllerDelegate;
#end
iMplementation
#import "MySplitViewController.h"
#implementation MySplitViewController
#synthesize rootViewControllerDelegate;
- (void)viewDidLoad
{
[super viewDidLoad];
for (UIViewController *viewController in self.viewControllers) {
if ([viewController respondsToSelector:#selector(setRootViewControllerDelegate:)]) {
[viewController setRootViewControllerDelegate:self.rootViewControllerDelegate];
}
}
}
#end
This simple implementation looks for child-view-controllers that accept a root-view-controller-delegate and hands it down. So when you want to add a "Show Login"-button to a certain (master- or detail-)view, just create your own UIViewController-subclass, add a #property id<RootViewControllerDelegate> rootViewControllerDelegate and associate an action like this with the button:
- (IBAction)loginButtonClicked:(id)sender {
UIStoryboard *mainSB=[UIStoryboard storyboardWithName:#"LoginStoryboard" bundle:nil];
NSString *animationDirection=kCATransitionFromTop;
UIDeviceOrientation currentOrientation=[[UIDevice currentDevice] orientation];
if (currentOrientation==UIDeviceOrientationLandscapeLeft) {
animationDirection=kCATransitionFromBottom;
}
[self.rootViewControllerDelegate switchToStoryboard:mainSB animationDirectionOrNil:animationDirection];
}
Feel free to adjust everything to your needs.
well here it is my friend. i created a ibaction in in btn and pushed the new view with modal option of the story board. them i plugged in the classes for the login view which also refers to constants that keeps the record strait. then after login is recognized i pushed a new view. bare in mind i was having the users create a password in their device and not importing it from the server. if you want to import it from the server it will be different.
here is the log in .h
#import <UIKit/UIKit.h>
#import "Constants.h"
#interface LogInViewController : UIViewController<UITextFieldDelegate>
#property (nonatomic) BOOL pinValidated;
#end
and here is the code for login .m
#import "LogInViewController.h"
#import "KeychainWrapper.h"
#interface LogInViewController ()
#end
#implementation LogInViewController
#synthesize pinValidated;
- (id)initWithNibName:(NSString *)nibNameOrNil bundle:(NSBundle *)nibBundleOrNil
{
self = [super initWithNibName:nibNameOrNil bundle:nibBundleOrNil];
if (self) {
// Custom initialization
}
return self;
}
- (void)didReceiveMemoryWarning
{
// Releases the view if it doesn't have a superview.
[super didReceiveMemoryWarning];
// Release any cached data, images, etc that aren't in use.
}
// Helper method to congregate the Name and PIN fields for validation.
- (BOOL)credentialsValidated
{
NSString *name = [[NSUserDefaults standardUserDefaults] stringForKey:USERNAME];
BOOL pin = [[NSUserDefaults standardUserDefaults] boolForKey:PIN_SAVED];
if (name && pin) {
return YES;
} else {
return NO;
}
}
- (void)presentAlertViewForPassword
{
// 1
BOOL hasPin = [[NSUserDefaults standardUserDefaults] boolForKey:PIN_SAVED];
// 2
if (hasPin) {
// 3
NSString *user = [[NSUserDefaults standardUserDefaults] stringForKey:USERNAME];
NSString *message = [NSString stringWithFormat:#"What is %#'s password?", user];
UIAlertView *alert = [[UIAlertView alloc] initWithTitle:#"Enter Password"
message:message
delegate:self
cancelButtonTitle:#"Cancel"
otherButtonTitles:#"Done", nil];
// 4
[alert setAlertViewStyle:UIAlertViewStyleSecureTextInput]; // Gives us the password field
alert.tag = kAlertTypePIN;
// 5
UITextField *pinField = [alert textFieldAtIndex:0];
pinField.delegate = self;
pinField.autocapitalizationType = UITextAutocapitalizationTypeWords;
pinField.tag = kTextFieldPIN;
[alert show];
} else {
UIAlertView *alert = [[UIAlertView alloc] initWithTitle:#"Setup Credentials"
message:#"Enter Your information!"
delegate:self
cancelButtonTitle:#"Cancel"
otherButtonTitles:#"Done", nil];
// 6
[alert setAlertViewStyle:UIAlertViewStyleLoginAndPasswordInput];
alert.tag = kAlertTypeSetup;
UITextField *nameField = [alert textFieldAtIndex:0];
nameField.autocapitalizationType = UITextAutocapitalizationTypeWords;
nameField.placeholder = #"Name"; // Replace the standard placeholder text with something more applicable
nameField.delegate = self;
nameField.tag = kTextFieldName;
UITextField *passwordField = [alert textFieldAtIndex:1]; // Capture the Password text field since there are 2 fields
passwordField.delegate = self;
passwordField.tag = kTextFieldPassword;
[alert show];
}
}
- (void)alertView:(UIAlertView *)alertView didDismissWithButtonIndex:(NSInteger)buttonIndex
{
if (alertView.tag == kAlertTypePIN) {
if (buttonIndex == 1 && self.pinValidated) { // User selected "Done"
[self performSegueWithIdentifier:#"ScoutSegue" sender:self];
self.pinValidated = NO;
} else { // User selected "Cancel"
[self presentAlertViewForPassword];
}
} else if (alertView.tag == kAlertTypeSetup) {
if (buttonIndex == 1 && [self credentialsValidated]) { // User selected "Done"
[self performSegueWithIdentifier:#"ScoutSegue" sender:self];
} else { // User selected "Cancel"
[self presentAlertViewForPassword];
}
}
}
#pragma mark - Text Field + Alert View Methods
- (void)textFieldDidEndEditing:(UITextField *)textField
{
// 1
switch (textField.tag) {
case kTextFieldPIN: // We go here if this is the 2nd+ time used (we've already set a PIN at Setup).
NSLog(#"User entered PIN to validate");
if ([textField.text length] > 0) {
// 2
NSUInteger fieldHash = [textField.text hash]; // Get the hash of the entered PIN, minimize contact with the real password
// 3
if ([KeychainWrapper compareKeychainValueForMatchingPIN:fieldHash]) { // Compare them
NSLog(#"** User Authenticated!!");
self.pinValidated = YES;
} else {
NSLog(#"** Wrong Password :(");
self.pinValidated = NO;
}
}
break;
case kTextFieldName: // 1st part of the Setup flow.
NSLog(#"User entered name");
if ([textField.text length] > 0) {
[[NSUserDefaults standardUserDefaults] setValue:textField.text forKey:USERNAME];
[[NSUserDefaults standardUserDefaults] synchronize];
}
break;
case kTextFieldPassword: // 2nd half of the Setup flow.
NSLog(#"User entered PIN");
if ([textField.text length] > 0) {
NSUInteger fieldHash = [textField.text hash];
// 4
NSString *fieldString = [KeychainWrapper securedSHA256DigestHashForPIN:fieldHash];
NSLog(#"** Password Hash - %#", fieldString);
// Save PIN hash to the keychain (NEVER store the direct PIN)
if ([KeychainWrapper createKeychainValue:fieldString forIdentifier:PIN_SAVED]) {
[[NSUserDefaults standardUserDefaults] setBool:YES forKey:PIN_SAVED];
[[NSUserDefaults standardUserDefaults] synchronize];
NSLog(#"** Key saved successfully to Keychain!!");
}
}
break;
default:
break;
}
}
#pragma mark - View lifecycle
- (void)viewDidLoad
{
[super viewDidLoad];
self.pinValidated = NO;
}
- (void)viewDidAppear:(BOOL)animated
{
[super viewDidAppear:animated];
[self presentAlertViewForPassword];
}
- (void)viewDidUnload
{
[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 == UIInterfaceOrientationPortrait);
}
#end
here is the code for the constant
// Used for saving to NSUserDefaults that a PIN has been set, and is the unique identifier for the Keychain.
#define PIN_SAVED #"hasSavedPIN"
// Used for saving the user's name to NSUserDefaults.
#define USERNAME #"username"
// Used to specify the application used in accessing the Keychain.
#define APP_NAME [[[NSBundle mainBundle] infoDictionary] objectForKey:#"CFBundleIdentifier"]
// Used to help secure the PIN.
// Ideally, this is randomly generated, but to avoid the unnecessary complexity and overhead of storing the Salt separately, we will standardize on this key.
// !!KEEP IT A SECRET!!
#define SALT_HASH #"FvTivqTqZXsgLLx1v3P8TGRyVHaSOB1pvfm02wvGadj7RLHV8GrfxaZ84oGA8RsKdNRpxdAojXYg9iAj"
// Typedefs just to make it a little easier to read in code.
typedef enum {
kAlertTypePIN = 0,
kAlertTypeSetup
} AlertTypes;
typedef enum {
kTextFieldPIN = 1,
kTextFieldName,
kTextFieldPassword
} TextFieldTypes;
here is the keychainwrapper
#import <Foundation/Foundation.h>
#import <Security/Security.h>
#import <CommonCrypto/CommonHMAC.h>
#interface KeychainWrapper : NSObject
// Generic exposed method to search the keychain for a given value. Limit one result per search.
+ (NSData *)searchKeychainCopyMatchingIdentifier:(NSString *)identifier;
// Calls searchKeychainCopyMatchingIdentifier: and converts to a string value.
+ (NSString *)keychainStringFromMatchingIdentifier:(NSString *)identifier;
// Simple method to compare a passed in hash value with what is stored in the keychain.
// Optionally, we could adjust this method to take in the keychain key to look up the value.
+ (BOOL)compareKeychainValueForMatchingPIN:(NSUInteger)pinHash;
// Default initializer to store a value in the keychain.
// Associated properties are handled for you - setting Data Protection Access, Company Identifer (to uniquely identify string, etc).
+ (BOOL)createKeychainValue:(NSString *)value forIdentifier:(NSString *)identifier;
// Updates a value in the keychain. If you try to set the value with createKeychainValue: and it already exists,
// this method is called instead to update the value in place.
+ (BOOL)updateKeychainValue:(NSString *)value forIdentifier:(NSString *)identifier;
// Delete a value in the keychain.
+ (void)deleteItemFromKeychainWithIdentifier:(NSString *)identifier;
// Generates an SHA256 (much more secure than MD5) hash.
+ (NSString *)securedSHA256DigestHashForPIN:(NSUInteger)pinHash;
+ (NSString*)computeSHA256DigestForString:(NSString*)input;
#end
and finally here is the code for the keychainwrapper .m
#import "KeychainWrapper.h"
#import "Constants.h"
#implementation KeychainWrapper
// *** NOTE *** This class is ARC compliant - any references to CF classes must be paired with a "__bridge" statement to
// cast between Objective-C and Core Foundation Classes. WWDC 2011 Video "Introduction to Automatic Reference Counting" explains this.
// *** END NOTE ***
+ (NSMutableDictionary *)setupSearchDirectoryForIdentifier:(NSString *)identifier {
// Setup dictionary to access keychain.
NSMutableDictionary *searchDictionary = [[NSMutableDictionary alloc] init];
// Specify we are using a password (rather than a certificate, internet password, etc).
[searchDictionary setObject:( id)kSecClassGenericPassword forKey:( id)kSecClass];
// Uniquely identify this keychain accessor.
[searchDictionary setObject:APP_NAME forKey:( id)kSecAttrService];
// Uniquely identify the account who will be accessing the keychain.
NSData *encodedIdentifier = [identifier dataUsingEncoding:NSUTF8StringEncoding];
[searchDictionary setObject:encodedIdentifier forKey:( id)kSecAttrGeneric];
[searchDictionary setObject:encodedIdentifier forKey:( id)kSecAttrAccount];
return searchDictionary;
}
+ (NSData *)searchKeychainCopyMatchingIdentifier:(NSString *)identifier
{
NSMutableDictionary *searchDictionary = [self setupSearchDirectoryForIdentifier:identifier];
// Limit search results to one.
[searchDictionary setObject:( id)kSecMatchLimitOne forKey:( id)kSecMatchLimit];
// Specify we want NSData/CFData returned.
[searchDictionary setObject:( id)kCFBooleanTrue forKey:( id)kSecReturnData];
// Search.
NSData *result = nil;
CFTypeRef foundDict = NULL;
OSStatus status = SecItemCopyMatching(( CFDictionaryRef)searchDictionary, &foundDict);
if (status == noErr) {
result = ( NSData *)foundDict;
} else {
result = nil;
}
return result;
}
+ (NSString *)keychainStringFromMatchingIdentifier:(NSString *)identifier
{
NSData *valueData = [self searchKeychainCopyMatchingIdentifier:identifier];
if (valueData) {
NSString *value = [[NSString alloc] initWithData:valueData
encoding:NSUTF8StringEncoding];
return value;
} else {
return nil;
}
}
+ (BOOL)createKeychainValue:(NSString *)value forIdentifier:(NSString *)identifier
{
NSMutableDictionary *dictionary = [self setupSearchDirectoryForIdentifier:identifier];
NSData *valueData = [value dataUsingEncoding:NSUTF8StringEncoding];
[dictionary setObject:valueData forKey:( id)kSecValueData];
// Protect the keychain entry so it's only valid when the device is unlocked.
[dictionary setObject:( id)kSecAttrAccessibleWhenUnlocked forKey:( id)kSecAttrAccessible];
// Add.
OSStatus status = SecItemAdd(( CFDictionaryRef)dictionary, NULL);
// If the addition was successful, return. Otherwise, attempt to update existing key or quit (return NO).
if (status == errSecSuccess) {
return YES;
} else if (status == errSecDuplicateItem){
return [self updateKeychainValue:value forIdentifier:identifier];
} else {
return NO;
}
}
+ (BOOL)updateKeychainValue:(NSString *)value forIdentifier:(NSString *)identifier
{
NSMutableDictionary *searchDictionary = [self setupSearchDirectoryForIdentifier:identifier];
NSMutableDictionary *updateDictionary = [[NSMutableDictionary alloc] init];
NSData *valueData = [value dataUsingEncoding:NSUTF8StringEncoding];
[updateDictionary setObject:valueData forKey:( id)kSecValueData];
// Update.
OSStatus status = SecItemUpdate(( CFDictionaryRef)searchDictionary,
( CFDictionaryRef)updateDictionary);
if (status == errSecSuccess) {
return YES;
} else {
return NO;
}
}
+ (void)deleteItemFromKeychainWithIdentifier:(NSString *)identifier
{
NSMutableDictionary *searchDictionary = [self setupSearchDirectoryForIdentifier:identifier];
CFDictionaryRef dictionary = ( CFDictionaryRef)searchDictionary;
//Delete.
SecItemDelete(dictionary);
}
+ (BOOL)compareKeychainValueForMatchingPIN:(NSUInteger)pinHash
{
if ([[self keychainStringFromMatchingIdentifier:PIN_SAVED] isEqualToString:[self securedSHA256DigestHashForPIN:pinHash]]) {
return YES;
} else {
return NO;
}
}
// This is where most of the magic happens (the rest of it happens in computeSHA256DigestForString: method below).
// Here we are passing in the hash of the PIN that the user entered so that we can avoid manually handling the PIN itself.
// Then we are extracting the username that the user supplied during setup, so that we can add another unique element to the hash.
// From there, we mash the user name, the passed-in PIN hash, and the secret key (from ChristmasConstants.h) together to create
// one long, unique string.
// Then we send that entire hash mashup into the SHA256 method below to create a "Digital Digest," which is considered
// a one-way encryption algorithm. "One-way" means that it can never be reverse-engineered, only brute-force attacked.
// The algorthim we are using is Hash = SHA256(Name + Salt + (Hash(PIN))). This is called "Digest Authentication."
+ (NSString *)securedSHA256DigestHashForPIN:(NSUInteger)pinHash
{
// 1
NSString *name = [[NSUserDefaults standardUserDefaults] stringForKey:USERNAME];
name = [name stringByAddingPercentEscapesUsingEncoding:NSUTF8StringEncoding];
// 2
NSString *computedHashString = [NSString stringWithFormat:#"%#%i%#", name, pinHash, SALT_HASH];
// 3
NSString *finalHash = [self computeSHA256DigestForString:computedHashString];
NSLog(#"** Computed hash: %# for SHA256 Digest: %#", computedHashString, finalHash);
return finalHash;
}
// This is where the rest of the magic happens.
// Here we are taking in our string hash, placing that inside of a C Char Array, then parsing it through the SHA256 encryption method.
+ (NSString*)computeSHA256DigestForString:(NSString*)input
{
const char *cstr = [input cStringUsingEncoding:NSUTF8StringEncoding];
NSData *data = [NSData dataWithBytes:cstr length:input.length];
uint8_t digest[CC_SHA256_DIGEST_LENGTH];
// This is an iOS5-specific method.
// It takes in the data, how much data, and then output format, which in this case is an int array.
CC_SHA256(data.bytes, data.length, digest);
// Setup our Objective-C output.
NSMutableString* output = [NSMutableString stringWithCapacity:CC_SHA256_DIGEST_LENGTH * 2];
// Parse through the CC_SHA256 results (stored inside of digest[]).
for(int i = 0; i < CC_SHA256_DIGEST_LENGTH; i++) {
[output appendFormat:#"%02x", digest[i]];
}
return output;
}
#end
this is the blueprint for creating a login view before any other view weather it be view controller or any other view such as tab bar or so. take a good look at this set of codes and modify them as you please. hope this helps you my friend. the codes are there all you have to do is study them and modify them to what you want. happy coding.

Hide Copy and Deselect UITextView options after copying all text

I am working on a messaging app. I want to give a "copy" option to the user when they enter their message in a UITextView. When the user presses the "copy" button, it is copying the message, but the popover shows again and again, and the text is still selectable.
I don't know how to control this. I have pasted some source code for your reference.
I wrote a sub class for UITextView.
- (BOOL)canPerformAction:(SEL)action withSender:(id)sender
{
NSLog(#"Action : %#", NSStringFromSelector(action));
NSLog(#"Sender : %#", sender);
if (action == #selector(copy:))
{
[self selectAll:self];
//return [super canPerformAction:action withSender:sender];
return YES;
}
else if (action == #selector(cut:))
{
return NO;
}
return NO;
}
I have solved my problem. I have used below codes to solve.
- (BOOL)canPerformAction:(SEL)action withSender:(id)sender
{
if (action == #selector(copy:))
{
[self selectAll:self];
return YES;
}
else if (action == #selector(cut:))
{
return NO;
}
return NO;
}
- (void)copy:(id)sender
{
UIPasteboard *pastBoard = [UIPasteboard generalPasteboard];
[pastBoard setString:self.text];
self.selectedTextRange = nil;
[self resignFirstResponder];
}
Thanks to Mr.Vimal Venugopalan and Mr.Mrueg. It is working for me. It will help to some one.
If you are using the iOS5
UITextView adopts the UITextInput protocol, which has a selectedTextRange property. Set the property to nil:
Add the below code just above the last return NO.
self.selectedTextRange = nil;
Hope this helps

Converting One-Off Code to Method

I have the following code which takes a touch on one button and draws a border around that button, then makes sure that all the other buttons have no border (8 buttons total). This is a method in a singleton class called AnswerButtons. This code works fine.
- (IBAction)button1WasTouched:(id)sender {
NSLog(#"Hello from button 1");
// retrieve, modify and update clueAnsState
NSMutableArray *newCAS = [[GameData gameData].curData objectForKey:#"clueAnsState"];
[newCAS replaceObjectAtIndex:0
withObject:#"2"];
[[GameData gameData].curData setObject:newCAS
forKey:#"clueAnsState"];
// Highlight the pressed button & make sure other buttons are not highlighted
for (NSInteger idx = 0; idx < 8; idx++) {
NSString *temp = [newCAS objectAtIndex:idx];
if ([temp isEqualToString:#"1"]) {
UIButton *b = [[AnswerButtons answerButtons].buttons objectAtIndex:idx];
[[b layer] setBorderWidth:0.0f];
}
if ([temp isEqualToString:#"2"]) {
UIButton *b = [[AnswerButtons answerButtons].buttons objectAtIndex:idx];
[[b layer] setBorderWidth:2.0f];
}
}
}
Now, I need to use this code for all 8 buttons, so I should write a method with one argument, the button number to modify (pos). In the singleton class .m I put which is virtually the same code:
- (void)activateAnswerAtPos:(int)pos {
// retrieve, modify and update clueAnsState
NSMutableArray *newCAS = [[GameData gameData].curData objectForKey:#"clueAnsState"];
[newCAS replaceObjectAtIndex:pos
withObject:#"2"];
[[GameData gameData].curData setObject:newCAS
forKey:#"clueAnsState"];
NSLog(#"%#", newCAS);
for (NSInteger idx = 0; idx < 8; idx++) {
NSString *temp = [newCAS objectAtIndex:idx];
if ([temp isEqualToString:#"1"]) {
UIButton *b = [[AnswerButtons answerButtons].buttons objectAtIndex:idx];
[[b layer] setBorderWidth:0.0f];
}
if ([temp isEqualToString:#"2"]) {
UIButton *b = [[AnswerButtons answerButtons].buttons objectAtIndex:idx];
[[b layer] setBorderWidth:2.0f];
}
}
}
So I changed the first code chunk to make it a call to the new method:
- (IBAction)button1WasTouched:(id)sender {
NSLog(#"Hello from button 1");
[sender activateAnswerAtPos:0];
}
Unfortunately, I'm doing something wrong as I get the following exception:
2012-03-30 19:41:40.199 P3[6751:f803] Hello from button 1
2012-03-30 19:41:40.201 P3[6751:f803] -[UIRoundedRectButton activateAnswerAtPos:]: unrecognized selector sent to instance 0x6e709f0
I'm not sure what's going on here; several alternatives don't work either and I think my troubleshooting is sending me in the wrong direction. What's wrong with the way I am calling this method? Clearly I'm not even getting to run the method. TIA.
It's hard to follow what you are trying to do but I would probably send all button actions to one method like this
- (void)buttonTapped:(UIButton *)buttonTapped;
{
NSArray *buttons = [AnswerButtons answerButtons].buttons;
// some kind of switch statement of logic to perform options depending on which button
// Can use the following to get the index
// NSInteger buttonIndex = [buttons indexOfObject:buttonTapped]
for (UIButton *button in buttons) {
if (button == buttonTapped) {
// highlight
} else {
// remove highlight
}
}
}
You're calling -activateAnswerAtPos: on sender, which is the button that was touched. You should instead call it on the instance of the class that defines the -activateAnswerAtPos: method. It's not clear from your code what that is, but my guess is self:
[self activateAnswerAtPos:0];

Resign first responder from separate class

I have a class that makes a keyboard toolbar which has "Next", "Previous", and "Done" buttons on it. Is there a way for this class to know (or find out) what objects are on the screen at any time?
E.g., can it see what the current view is and what the text fields on it are, and then be able to resign the first responder?
If you specifically want to resign first responder without the need to known which view is the first responder you can send resignFirstResponder to "nil" like this:
[[UIApplication sharedApplication] sendAction:#selector(resignFirstResponder) to:nil from:nil forEvent:nil];
This is documented behaviour although I cannot find in the docs right now.
Is there a way for this class to know
(or find out) what objects are on the
screen at the time?
Find the momma view and you can iterate through all the objects on the screen (because they will be UIViews too) like this. Note that you may need to add recursion:
for (UIView *view in mommaView.subviews) {
do something to the view
}
You can start at the Window class and go down from there, asking [view respondsTo:#selector(isFirstResponder) && [view isFirstResponder] on each. Some debugging code that I use might come in handy as a template and also while debugging:
+ (void) dumpWindowFrom:(NSString *) fromText {
[self dumpViews:[[UIApplication sharedApplication] keyWindow] from:fromText];
}
void dumpViewsRecursive(UIView* view, NSString *text, NSString *indent)
{
Class cl = [view class];
NSString *classDescription = [cl description];
// while ([cl superclass]) //restore to print superclass list
// {
// cl = [cl superclass];
// classDescription = [classDescription stringByAppendingFormat:#":%#", [cl description]];
// }
if ([text compare:#""] == NSOrderedSame)
NSLog(#"%d: %# %# %#", (int)view, classDescription, NSStringFromCGRect(view.frame), view.hidden ? #"Inv" : #"Vis");
else
NSLog(#"%d: %# %# %# %#", (int)view, text, classDescription, NSStringFromCGRect(view.frame), view.hidden ? #"Inv" : #"Vis");
for (NSUInteger i = 0; i < [view.subviews count]; i++)
{
UIView *subView = [view.subviews objectAtIndex:i];
NSString *newIndent = [[NSString alloc] initWithFormat:#" %#", indent];
NSString *msg = [[NSString alloc] initWithFormat:#"%#%d:", newIndent, i];
dumpViewsRecursive (subView, msg, newIndent);
[msg release];
[newIndent release];
}
}
+ (void) dumpViews: (UIView *) view {
dumpViewsRecursive (( (!view) ? [[UIApplication sharedApplication] keyWindow] : view), #"" ,#"");
}
+ (void) dumpViews: (UIView *) view from:(NSString *) fromText{
dumpViewsRecursive ((!view) ? [[UIApplication sharedApplication] keyWindow] : view, fromText, #"");
}
yes, the methods provided below will be called whenever a textField becomes Active. I think you are looking for
- (BOOL) textFieldShouldReturn:(UITextField *)textField
{
[textField resignFirstResponder];
return 1;
}
or
- (void) textFieldDidBeginEditing:(UITextField *)textField
{
[textField resignFirstResponder];
}
- (void) textFieldDidEndEditing:(UITextField *)textField
{
[textField resignFirstResponder];
}
and if you are looking for a specific textField in your view, you should assign them tags:
textField.tag =1 // for textField 1
textField.tag =2 // for textField 2
// You may check for these tags and then resign specific ones.