Delegation

UITextField

- (void)loadView
{
    CGRect frame = [UIScreen mainScreen].bounds;
    BNRHypnosisView *backgroundView = [[BNRHypnosisView alloc] initWithFrame:frame];

    CGRect textFieldRect = CGRectMake(40, 70, 240, 30);
    UITextField *textField = [[UITextField alloc] initWithFrame:textFieldRect];

    // Setting the border style on the text field will allow us to see it more easily
    textField.borderStyle = UITextBorderStyleRoundedRect;
    [backgroundView addSubview:textField];

    self.view = backgroundView;
}

UIResponder

UIResponder is an abstract class in the UIKit framework. It is the superclass of three classes that you have already encountered:

  • UIView
  • UIViewController
  • UIApplication

UIResponder是个抽象类,UIView,UIViewController和UIApplication都实现了此类。

UIResponder defines methods for handling (or “responding to”) events: touch events, motion events (like a shake), and remote control events (like pausing or playing). Subclasses override these methods to customize how they respond to events.

UIResponder定义了事件响应方法,响应touch, motion, remote control事件,其子类可以重写这些事件响应方法。

With touch events, it is obvious which view the user has touched. Touch events are sent directly to that view.

What about the other types of events? The UIWindow has a pointer called firstResponder which indicates who should respond to the other types of events. When you select a text field, for example, the window moves its firstResponder pointer to that text field. Motion and remote control events are sent to the first responder.

touch事件会直接发送给被触摸的视图。
UIWindow中有个指针叫firstResponder,来指明谁来响应motion, remote control事件。当选中一个text field,firstResponder指针会指向这个text field。

When a text field or a text view becomes firstResponder, it shows its keyboard. When it loses first responder status, it hides its keyboard. If you want one of these views to become first responder, you send it the message becomeFirstResponder and the keyboard appears. When you want to hide the keyboard, you send it the message resignFirstResponder.

当text filed变成firstResponder,会显示键盘,当其不再是firstResponder,键盘隐藏。
如果想让某视图变成firstResponder,可以调用该视图的becomeFirstResponder,反之调用resignFirstResponder

Keyboard

The keyboard’s appearance is determined by a set of the UITextField’s properties called UITextInputTraits.

property description
autocapitalizationType This determines how capitalization is handled. The options are none, words, sentences, or all characters.
autocorrectionType This will suggest and correct unknown words. This value can be YES or NO.
enablesReturnKeyAutomatically This value can be YES or NO. If set to yes, the return key will be disabled if no text has been typed. As soon as any text is entered, the return key becomes enabled.
keyboardType This determines the type of keyboard that will be displayed. Some examples are the ASCII keyboard, email address keyboard, number pad, and the URL keyboard.
secureTextEntry Setting this to YES makes the text field behave like a password field, hiding the text that is entered.
- (void)loadView
{
    CGRect frame = [UIScreen mainScreen].bounds;
    BNRHypnosisView *backgroundView = [[BNRHypnosisView alloc] initWithFrame:frame];

    CGRect textFieldRect = CGRectMake(40, 70, 240, 30);
    UITextField *textField = [[UITextField alloc] initWithFrame:textFieldRect];

    // Setting the border style on the text field will allow us to see it more easily
    textField.borderStyle = UITextBorderStyleRoundedRect;
    textField.placeholder = @"Hypnotize me";
    // 设置键盘的retrun key type为Done
    // 键盘的return key:键盘输入完成后,点击return key完成输入,隐藏键盘
    textField.returnKeyType = UIReturnKeyDone;

    [backgroundView addSubview:textField];
    self.view = backgroundView;
}

Delegation pattern

You have already seen the Target-Action pattern. This is one form of callbacks that is used by UIKit: When a button is tapped, it sends its action message to its target.

A button’s life is relatively simple. For objects with more complex lives, like a text field, Apple uses the delegation pattern.

代理模式,为UITextField对象设置代理,由代理来处理UITextField的各种消息。

代理要为text filed对象实现的一些方法如下:

- (BOOL)textFieldShouldEndEditing:(UITextField *)textField;
- (BOOL)textFieldShouldBeginEditing:(UITextField *)textField;
- (BOOL)textFieldShouldClear:(UITextField *)textField;
- (BOOL)textFieldShouldReturn:(UITextField *)textField;

设置text filed的delegate属性,指定其代理

 // BNRHypnosisViewController
- (void)loadView
{
    CGRect frame = [UIScreen mainScreen].bounds;
    BNRHypnosisView *backgroundView = [[BNRHypnosisView alloc] initWithFrame:frame];

    CGRect textFieldRect = CGRectMake(40, 70, 240, 30);
    UITextField *textField = [[UITextField alloc] initWithFrame:textFieldRect];

    // Setting the border style on the text field will allow us to see it more easily
    textField.borderStyle = UITextBorderStyleRoundedRect;
    textField.placeholder = @"Hypnotize me";
    // 设置键盘的retrun key type为Done
    textField.returnKeyType = UIReturnKeyDone;

    // 为text filed设置delegate
    textField.delegate = self;

    [backgroundView addSubview:textField];
    self.view = backgroundView;
}

Protocol

代理需要为被代理对象实现哪些方法呢?这由protocol来定义,protocol就像JAVA中的接口。

The protocol for UITextField’s delegate looks like this:

// The NSObject in angled brackets refers to the NSObject protocol and tells us 
// that UITextFieldDelegate includes all of the methods in the NSObject protocol.
@protocol UITextFieldDelegate 

@optional
- (BOOL)textFieldShouldBeginEditing:(UITextField *)textField;
- (void)textFieldDidBeginEditing:(UITextField *)textField;
- (BOOL)textFieldShouldEndEditing:(UITextField *)textField;
- (void)textFieldDidEndEditing:(UITextField *)textField;
- (BOOL)textField:(UITextField *)textField shouldChangeCharactersInRange:(NSRange)range replacementString:(NSString *)string;
- (BOOL)textFieldShouldClear:(UITextField *)textField;
- (BOOL)textFieldShouldReturn:(UITextField *)textField;

@end

Note that a protocol is not a class; it is simply a list of method declarations.

Methods declared in a protocol can be required or optional. By default, protocol methods are required. If a protocol has optional methods, these are preceded by the directive @optional.

Before sending an optional message, the object first asks its delegate if it is okay to send that message by sending another message, respondsToSelector:. Every object implements this method, which checks at runtime whether an object implements a given method. You can turn a method selector into a value that you can pass as an argument with the @selector() directive. For example, UITextField could implement a method that looks like this:

- (void)clearButtonTapped
{
    // textFieldShouldClear: is an optional method, so we check first
    SEL clearSelector = @selector(textFieldShouldClear:);
    // 先检查对应的方法是否存在,如果存在再调用
    if ([self.delegate respondsToSelector:clearSelector]) {
        if ([self.delegate textFieldShouldClear:self]) {
            self.text = @"";
        }
    }
}

protocol不是一个类,只是声明了一些抽像方法。
protocol中的方法默认是required,就是代理必须要实现的;可以用@optional来声明为可选的。
被代理的对象在调用protocol中声明的optional方法前,会先通过respondsToSelector:方法来确认代理是否实现了对应的optional message(OC中叫message,可以理解为java中的method)。

**
对象,代理(delegate),协议(protocol),这三者的关系要理清楚:对象(比如一个text field)有一个对应的protocol,用来定义text field对象的delegate应该实现的方法,delegate要去实现protocol中定义的方法。text field对象会去调用delegate中实现的方法。
**

But for the compiler to know to check for implementations of a protocol’s required methods, the class must explicitly state that it conforms to a protocol. This is done either in the class header file or the class extension: the protocols that a class conforms to are added to a comma-delimited list inside angled brackets in the interface declaration.

delegate要标明实现哪些protocol,可以头文件或是在class extension中声明实现的protocol,用尖括号括起来,如果有多个protocol用逗号分隔。

在头文件和class extension中的区别如之前讲到的,头文件中的是public的,class extension中的是private的。

The reason for adding it to the class extension rather than the header file is the same reason as always: add to the class extension if the information (conforming to a particular protocol in this case) does not need to be publicly visible, and add it to the header file if other objects do need to know about the information.

以下是在class extension中标明:

@interface BNRHypnosisViewController () 
@end

Motion Effects

苹果设备上,在首页倾斜屏幕,可以看到首页的图标会移动。这可以通过UIInterpolatingMotionEffect来实现:

UIInterpolatingMotionEffect *motionEffect;

motionEffect = [[UIInterpolatingMotionEffect alloc] initWithKeyPath:@"center.x" type:UIInterpolatingMotionEffectTypeTiltAlongHorizontalAxis];
motionEffect.minimumRelativeValue = @(-25);
motionEffect.maximumRelativeValue = @(25);
[messageLabel addMotionEffect:motionEffect];

motionEffect = [[UIInterpolatingMotionEffect alloc] initWithKeyPath:@"center.y" type:UIInterpolatingMotionEffectTypeTiltAlongVerticalAxis];
motionEffect.minimumRelativeValue = @(-25);
motionEffect.maximumRelativeValue = @(25);
[messageLabel addMotionEffect:motionEffect];

main() and UIApplication

iOS应用的程序入口,如下:

int main(int argc, char *argv[])
{
    @autoreleasepool {
        return UIApplicationMain(argc, argv, nil, NSStringFromClass([BNRAppDelegate class]));
    }
}

The function UIApplicationMain creates an instance of a class called UIApplication. For every application, there is a single UIApplication instance. This object is responsible for maintaining the run loop. Once the application object is created, its run loop essentially becomes an infinite loop: the executing thread will never return to main().

UIApplicationMain 方法创建了一个 UIApplication 实例,一个应用只有一个此实例,负责维护应用的run loop。

Another thing the function UIApplicationMain does is create an instance of the class that will serve as the UIApplication’s delegate. Notice that the final argument to the UIApplicationMain function is an NSString that is the name of the delegate’s class. So, this function will create an instance of BNRAppDelegate and set it as the delegate of the UIApplication object.

UIApplicationMain 方法不仅创建了UIApplication 实例,还会为其创建对应的delegate,UIApplicationMain 方法的最后一个参数就是delegate's name。

The first event added to the run loop in every application is a special “kick-off” event that triggers the application to send a message to its delegate. This message is application:didFinishLaunchingWithOptions:. You implemented this method in BNRAppDelegate.m to create the window and the controller objects used in this application.

然后application调用其代理的application:didFinishLaunchingWithOptions:方法来完成窗口及相应视图的创建,我们在此方法添加自己的视图。


本文是对《iOS Programming The Big Nerd Ranch Guide 4th Edition》第七章的总结。

你可能感兴趣的:(Delegation)