简体   繁体   中英

Objective C : global methods textField

Is there any way to make the following methods "global":

- (void)textFieldDidBeginEditing:(UITextField *)textField{
     //some code here
}
- (void)textFieldDidEndEditing:(UITextField *)textField{
     //some code here
}
- (BOOL)textFieldShouldReturn:(UITextField *)textField{
     //some code here
}

I explain, I have an Objective-C Project with ARC and Storyboard on and each of my views have their own View Controller Class. There is a lot of text field in my project and sometimes when the user start edit, the keyboard is hiding content. So I search on the web to find a way to handle this problem. The answer of this topic: Scrolling content hidden by keyboard. xcode 4.3.2 helped me a lot and it's working, it scrolls content so the user is always able to see the bottom of the view.

But now here is my problem, I would like to make this functionnality (content scrolling when start editing) be active every where on my projet. The unoptimized solution would be to copy the code in each of my views classes. But I am wondering if there is any way to import a class that contain thoses methods or something.

My actual code is almost the same as here (answer): Scrolling content hidden by keyboard. xcode 4.3.2

I hope there is some Objective-C guru somewhere that could help me with my trouble.

-EDIT-

Each of my views in Storyboard has his own UIViewControllerClass. Each of these with TextField have this code:

VCExample.h

@interface VCExample : UIViewController

@property (weak, nonatomic) IBOutlet UITextField *oneTextField;

@end

VCExample.m

#import "VCExample.h"
#import "TextFieldScroll.h"

@interface VCExample ()

@end

@implementation VCExample

- (void)viewDidLoad
{
    [super viewDidLoad];

    TextFieldScroll *myTextFieldClass = [[TextFieldScroll alloc] init];
    [self.usernameTextField setDelegate: myTextFieldClass];
    self.usernameTextField.delegate = [TextFieldScroll shared];
}

@end

TextFieldScroll.h

#import <Foundation/Foundation.h>

@interface TextFieldScroll : NSObject <UITextFieldDelegate>

+ (id)shared;

@property CGFloat animatedDistance;

@end

TextFieldScroll.m

#import "TextFieldScroll.h"

@implementation TextFieldScroll

@synthesize animatedDistance;

static const CGFloat KEYBOARD_ANIMATION_DURATION = 0.3;
static const CGFloat MINIMUM_SCROLL_FRACTION = 0.2;
static const CGFloat MAXIMUM_SCROLL_FRACTION = 0.8;
static const CGFloat PORTRAIT_KEYBOARD_HEIGHT = 216;
static const CGFloat LANDSCAPE_KEYBOARD_HEIGHT = 162;

+ (id)shared
{
    static TextFieldScroll *sharedMyTextFieldHandler = nil;
    static dispatch_once_t onceToken;

    dispatch_once(&onceToken, ^
                  {
                      sharedMyTextFieldHandler = [[self alloc] init];
                  });

    return sharedMyTextFieldHandler;
}

- (void)textFieldDidBeginEditing:(UITextField *)textField
{
    CGRect textFieldRect = [self.my.window convertRect:textField.bounds fromView:textField];
    CGRect viewRect = [self.view.window convertRect:self.view.bounds fromView:self.view];

    CGFloat midline = textFieldRect.origin.y + 0.5 * textFieldRect.size.height;
    CGFloat numerator =
    midline - viewRect.origin.y
    - MINIMUM_SCROLL_FRACTION * viewRect.size.height;
    CGFloat denominator =
    (MAXIMUM_SCROLL_FRACTION - MINIMUM_SCROLL_FRACTION)
    * viewRect.size.height;
    CGFloat heightFraction = numerator / denominator;

    if (heightFraction < 0.0)
    {
        heightFraction = 0.0;
    }
    else if (heightFraction > 1.0)
    {
        heightFraction = 1.0;
    }

    UIInterfaceOrientation orientation =
    [[UIApplication sharedApplication] statusBarOrientation];
    if (orientation == UIInterfaceOrientationPortrait ||
        orientation == UIInterfaceOrientationPortraitUpsideDown)
    {
        animatedDistance = floor(PORTRAIT_KEYBOARD_HEIGHT * heightFraction);
    }
    else
    {
        animatedDistance = floor(LANDSCAPE_KEYBOARD_HEIGHT * heightFraction);
    }

    CGRect viewFrame = self.view.frame;
    viewFrame.origin.y -= animatedDistance;

    [UIView beginAnimations:nil context:NULL];
    [UIView setAnimationBeginsFromCurrentState:YES];
    [UIView setAnimationDuration:KEYBOARD_ANIMATION_DURATION];

    [self.view setFrame:viewFrame];

    [UIView commitAnimations];

}

- (void)textFieldDidEndEditing:(UITextField *)textField
{
    CGRect viewFrame = self.view.frame;
    viewFrame.origin.y += animatedDistance;

    [UIView beginAnimations:nil context:NULL];
    [UIView setAnimationBeginsFromCurrentState:YES];
    [UIView setAnimationDuration:KEYBOARD_ANIMATION_DURATION];

    [self.view setFrame:viewFrame];

    [UIView commitAnimations];
}

- (BOOL)textFieldShouldReturn:(UITextField *)textField
{
    [textField resignFirstResponder];
    return YES;
}

The best way to do this would be to create a base class for all your view controllers that implements these methods with the keyboard animation code. Turn your existing view controllers into subclasses of that base class, and send the appropriate message to super in the subclasses' implementations of these methods.

Yes, technically you could make these methods global. This is how I would do it:

  1. Create a singleton NSObject subclass that responds to UITextFieldDelegate messages.

    The code for MyTextFieldHandler.h file:

     @interface MyTextFieldHandler : NSObject + (id)shared; @end 

    The code for MyTextFieldHandler.m file:

     + (id)shared { static MyTextFieldHandler *sharedMyTextFieldHandler = nil; static dispatch_once_t onceToken; dispatch_once(&onceToken, ^ { sharedMyTextFieldHandler = [[self alloc] init]; }); return sharedMyTextFieldHandler; } 
  2. Set all UITextField delegates to this object.

Write all the code that needs to be done when those messages are sent in the suclass.

There are more ways to do the step 2.

  1. One could simply set for all UITextField objects:

     specificTextField.delegate = [MyTextFieldHandler shared]; anotherSpecificTextField.delegate = [MyTextFieldHandler shared]; 
  2. Another way would be subclassing the UITextField and override awakeFromNib and initWithFrame: methods, which would now set the delegate with:

     self.delegate = [MyTextFieldHandler shared]; 
  3. The third way would be the hardest, but would not require any additional code for new UITextField added. You could swizzle any of the UITextField methods to set the delegate to singleton. Read this post from Mattt Thompson about method swizzling: http://nshipster.com/method-swizzling/

Which way is the best? Depends on your needs and what do you want to do.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM