你的位置:首页 > 操作系统

[操作系统]iOS 委托与文本输入(内容根据iOS编程编写)


  • 文本框(UITextField)

  本章节继续编辑 JXHypnoNerd 。文件地址 。

  首先我们继续编辑  JXHypnosisViewController.m 修改  loadView 方法,向  view 中添加一个 UITextField 对象:

#import "JXHypnosisViewController.h"#import "JXHypnosisView.h"@interface JXHypnosisViewController ()@end@implementation JXHypnosisViewController- (instancetype)initWithNibName:(NSString *)nibNameOrNil             bundle:(NSBundle *)nibBundleOrNil {  self = [super initWithNibName:nibNameOrNil bundle:nibBundleOrNil];  if (self) {    // 设置标签项的标题    self.tabBarItem.title = @"Hypnotize";        // 从图片文件创建一个 UIImage 对象    UIImage * i = [UIImage imageNamed:@"Hypno"];        // 将 UIImage 对象赋值给标签项的 iamge 属性    self.tabBarItem.image = i;  }  return self;}- (void)viewDidLoad {  [super viewDidLoad];    }- (void)loadView {  // 创建一个 JXHypnosisView 对象  JXHypnosisView * backgroundView = [[JXHypnosisView alloc] init];    CGRect textFieldRect = CGRectMake(40, 70, 240, 30);  UITextField * textField = [[UITextField alloc] init];  textField.frame = textFieldRect;    // 设置 UITextField 对象的边框样式,便于查看它在屏幕上的位置  textField.borderStyle = UITextBorderStyleRoundedRect;  [backgroundView addSubview:textField];    // 将 JXHypnosisView 对象赋给视图控制器的view 属性  self.view = backgroundView;}@end

  构建并运行应用,项目中会显示一个文本框,该文本框就是刚才我们添加的 UITextField 对象。点击文本框,这时屏幕底部就会弹出键盘,用于向文本框中输入文字,下面我们介绍一下第一响应者。

  • UIResponder

  UIResponder 是 UIKit 框架中的一个抽象类。之前我们了解过他的几个子类。

  1.   UIView
  2.   UIViewController
  3.       UIApplication 

  UIResponder 定义了一系列方法,用于接收和处理用户事件,例如 触摸事件、运动事件(摇晃设备)和功能控制事件(如编辑文本或者音乐播放)等。 UIResponder 的子类会覆盖这些方法,实现自己的事件响应代码。

  在以上事件中,触摸事件显然应该由被触摸的视图负责处理。系统会将触摸事件直接发送给被触摸的视图。

  其他类型的事件则会由第一响应者负责处理,UIWindow 有一个  firstResponder 属性指向第一响应者。例如,当用户点击 UITextField 对象时, UITextField 对象就会成为第一响应者。UIWindow 会将  firstResponder 指向该对象,之后,如果应用接收到运动事件和功能控制事件,都会发送给 UITextField 对象。

  当某个 UITextField 对象或 UITextView 对象成为第一响应者时,屏幕就会弹出键盘。除了用户点击之外,还可以在代码中向 UITextField 对象发送 becomeFirstResponder 消息,使其成为第一响应者。相反,如果要关闭键盘,则可以向 UITextField 对象发送  resignFirstResponder 消息,且要求改对象放弃第一响应者状态。一旦第一响应者不是 UITextField 对象,键盘就会消失。

  实际上,大部分视图都不需要成为第一响应者。例如 UISlider 对象,该对象只会处理触摸事件(用户拖拽滑块),而不会接收其他类型的事件,因此它不需要成为第一响应者。

  • 设置 UITextField 的键盘

  UITextField 对象有一系列属性,用于设置弹出的键盘。下面就修改这些属性,为 UITextField 对象添加占位符文本,并修改键盘的换行键盘类型。

#import "JXHypnosisViewController.h"#import "JXHypnosisView.h"@interface JXHypnosisViewController ()@end@implementation JXHypnosisViewController- (instancetype)initWithNibName:(NSString *)nibNameOrNil             bundle:(NSBundle *)nibBundleOrNil {  self = [super initWithNibName:nibNameOrNil bundle:nibBundleOrNil];  if (self) {    // 设置标签项的标题    self.tabBarItem.title = @"Hypnotize";        // 从图片文件创建一个 UIImage 对象    UIImage * i = [UIImage imageNamed:@"Hypno"];        // 将 UIImage 对象赋值给标签项的 iamge 属性    self.tabBarItem.image = i;  }  return self;}- (void)viewDidLoad {  [super viewDidLoad];    }- (void)loadView {  // 创建一个 JXHypnosisView 对象  JXHypnosisView * backgroundView = [[JXHypnosisView alloc] init];    CGRect textFieldRect = CGRectMake(40, 70, 240, 30);  UITextField * textField = [[UITextField alloc] init];  textField.frame = textFieldRect;    // 设置 UITextField 对象的边框样式,便于查看它在屏幕上的位置  textField.borderStyle = UITextBorderStyleRoundedRect;  // 修改占位符  textField.placeholder = @"Hypontize me";  // 修改键盘类型  textField.returnKeyType = UIReturnKeyDone;     [backgroundView addSubview:textField];    // 将 JXHypnosisView 对象赋给视图控制器的view 属性  self.view = backgroundView;}@end

  构建并运行,在程序中就会发现有一行占位符文本 Hypontize me ,当用户在程序中输入文字的时候,占位文本就会消失。换行键不再显示默认的 Return,而是 Done。

  但是,如果我们点击 Done 就会发现没有任何反应。实际上,修改换行键盘的类型知识改变了换行键的外观,如果需要实现用户点击换行键后的功能,必须实现编写响应的代码。在编写代码之前,在介绍 UITextField 对象中另外介个有用的属性

autocapitalizatioType

设置 UITextField 对象的自动大写功能,

包括 none(关闭自动大写)功能、words(单词)、sentences(句子)、allcharacters(所有字母)四种类型

autocorrectionType

启用/禁用(设置为YES/NO,下同)UITextField 对象的拼写建议功能,

可以提示用户输错的单词并提供修改建议

endblesReturnKeyAutomatically

启用/禁用 UITextField 对象的换行键自动检测功能。

如果将该属性设置为 YES ,UITextField 对象会自动监测用户输入,并根据是否输入了文字 启用/禁用 换行键。

keyboardType

设置 UITextField 对象弹出的键盘类型,

例如 ASCII 、 Capable(ASCII标准键盘)、 E-mail Address(电子邮件地址)Number Pad(数字键盘) 和 URL(网址)

secureTextEntry

启用/禁用 UITextField 对象的安全输入功能,

如果将该属性设置为 YES,UITextField 对象会以圆点代替用户输入的文字,类似于通常见到的密码文本框

  • 委托

  我们知道 目标-动作(Target-Action)设计模式。目标-动作 是 UIKit 中通常用的设计模式之一。目标-动作的工作方式为:当某个特定的事件发生时(例如按下按钮),发生事件的一方会向指定的目标对象发送一个之前设定好的动作消息。所有继承 UIControl 对象都可以设置目标-动作。

  在 目标-动作中,针对不同的事件,需要创建不同的动作消息。UIButton 对象的事件比较简单,通常只需要处理点击事件:相反,像 UITextField 这类事件复杂的对象,伟大的苹果使用了 委托设计模式 。UITextField 对象具有一个委托属性,通过为 UITextField 对象设置委托,UITextField 对象会在发生事件时向委托发送响应的消息,由委托处理该时间。例如,对于编辑 UITextField 对象文本内容的事件,有对象的委托方法。

@protocol UITextFieldDelegate <NSObject>@optional- (BOOL)textFieldShouldBeginEditing:(UITextField *)textField;    // return NO to disallow editing.- (void)textFieldDidBeginEditing:(UITextField *)textField;      // became first responder- (BOOL)textFieldShouldEndEditing:(UITextField *)textField;     // return YES to allow editing to stop and to resign first responder status. NO to disallow the editing session to end- (void)textFieldDidEndEditing:(UITextField *)textField;       // may be called if forced even if shouldEndEditing returns NO (e.g. view removed from window) or endEditing:YES called- (BOOL)textField:(UITextField *)textField shouldChangeCharactersInRange:(NSRange)range replacementString:(NSString *)string;  // return NO to not change text- (BOOL)textFieldShouldClear:(UITextField *)textField;        // called when clear button pressed. return NO to ignore (no notifications)- (BOOL)textFieldShouldReturn:(UITextField *)textField;       // called when 'return' key pressed. return NO to ignore.@end

  注意:在委托方法中,通常应该将对象自身作为第一个参数。多个对象可能具有相同的委托,当委托收到消息的时候,需要根据该参数判断发送该消息的对象。例如,如果某个视图控制器中包含多个 UITextField 对象,它们的委托都是该视图控制器,那么视图控制器就需要根据 textField 参数获取响应的 UITextField 对象并执行不同的操作。

  下面我们就将 UITextField 对象所位于的视图控制器- JXHypnosisViewController 设置为它的委托,并实现  textFieldShouldReturn: 委托方法,当用户点击 Done 按钮时,UITextField 对象就会调用该方法。

  打开  JXHypnosisViewController 修改  loadView 方法,将 UITextField 对象的委托属性设置为  JXHypnosisViewController 自身。

   textFieldShouldReturn: 只有一个参数,就是用户点击换行键的相应 UITextField 对象。目前,应用只会向控制台输出 UITextField 对象的文本内容。

  接下来在代码中实现  textFieldShouldReturn: 方法。

#import "JXHypnosisViewController.h"#import "JXHypnosisView.h"@interface JXHypnosisViewController ()@end@implementation JXHypnosisViewController- (instancetype)initWithNibName:(NSString *)nibNameOrNil             bundle:(NSBundle *)nibBundleOrNil {  self = [super initWithNibName:nibNameOrNil bundle:nibBundleOrNil];  if (self) {    // 设置标签项的标题    self.tabBarItem.title = @"Hypnotize";        // 从图片文件创建一个 UIImage 对象    UIImage * i = [UIImage imageNamed:@"Hypno"];        // 将 UIImage 对象赋值给标签项的 iamge 属性    self.tabBarItem.image = i;  }  return self;}- (void)viewDidLoad {  [super viewDidLoad];    }- (void)loadView {  // 创建一个 JXHypnosisView 对象  JXHypnosisView * backgroundView = [[JXHypnosisView alloc] init];    CGRect textFieldRect = CGRectMake(40, 70, 240, 30);  UITextField * textField = [[UITextField alloc] init];  textField.frame = textFieldRect;    // 设置 UITextField 对象的边框样式,便于查看它在屏幕上的位置  textField.borderStyle = UITextBorderStyleRoundedRect;  // 修改占位符  textField.placeholder = @"Hypontize me";  // 修改键盘类型  textField.returnKeyType = UIReturnKeyDone;  textField.delegate = self;    [backgroundView addSubview:textField];    // 将 JXHypnosisView 对象赋给视图控制器的view 属性  self.view = backgroundView;}- (BOOL)textFieldShouldReturn:(UITextField *)textField {  NSLog(@"%@",textField.text);  return YES;}@end

  构建并运行程序,就可以在控制台打印信息,但是这里会有一处警告,暂时先不管。

  请注意, JXHypnosisViewController 不需要实现 UITextField 对象的所有委托方法,UITextField 对象会在运行时检查委托方法是否实现了某个方法,如果没有实现 UITextField 对象就不会调用该方法。

  • 协议

  凡是支持委托的对象,其背后都有一个相应的协议,声明可以向该对象(例如:UITextField)的委托对象(例如:JXHypnosisViewController)发送的消息。委托对象(例如:JXHypnosisViewController)需要根据这个协议为其响应的事件实现响应的方法。如果一个类实现了某个协议中规定的方法,就称这个类遵守该协议。

  声明协议的语法是,使用 @protocol 指令开头,后跟协议的名称(例如:UITextFieldDelegate)。尖括号里的 NSObject 是指只 NSObject 协议,其作用是声明 UITextFieldDelegate 中包含 NSObject 协议的全部方法。接着声明新协议特有的方法,最后使用 @end 指令来结束。

  协议不是类,只是一组方法声明。不能为协议创建对象,或者添加实例变量。协议自身不实现方法,需要由遵守相应协议的类来实现。

  协议所声明的方法可以是必须的(required)或者是可选的(optional)。协议方法默认都是必须的,所以当我们自定义协议的时候最好设置一下。使用 @optional 指令,可以将写在该指令之后的方法去全部声明为可选的。

  发送方在发送可选方法之前,都会先向其委托发送另一个名为  respondsToSelector: 的消息。所有Objective-C 对象都从 NSObject 继承了  respondsToSelector: 方法,该方法能在运行时检查对象是否实现了指定的方法。 @selector() 指令可以将选择器(selector)转换成数值,以便将其作为参数进行传递。

- (void)clearButtonTapped {    // textFieldShouldClear:是可选方法,需要先检查委托是否实现了该方法  SEL clearSelector = @selector(textFieldShouldClear:);  if ([self.delegate respondsToSelector:clearSelector]) {    self.text = @"";  }}

  如果某个方法是必须的,那么发送发可以直接向其委托对象发送相应的消息,不用检查委托对象是否实现了该方法。这也就意味着,如果委托对象没有实现相应的方法,应用就会抛出位置选择器(unrecognized selector)异常,导致应用崩溃。

  为了防止问题,编译器会检查某个类是否实现了相关协议的必须方法。要让编译器能够执行此类检查,必须将相应的类声明为遵守指定的协议,其语法格式为:在头文件或类扩展 @interface 指令末尾,将类所遵守的协议以都好分隔的列表形式写在尖括号里。

#import "JXHypnosisViewController.h"#import "JXHypnosisView.h"@interface JXHypnosisViewController ()<UITextFieldDelegate>@end@implementation JXHypnosisViewController- (instancetype)initWithNibName:(NSString *)nibNameOrNil             bundle:(NSBundle *)nibBundleOrNil {  self = [super initWithNibName:nibNameOrNil bundle:nibBundleOrNil];  if (self) {    // 设置标签项的标题    self.tabBarItem.title = @"Hypnotize";        // 从图片文件创建一个 UIImage 对象    UIImage * i = [UIImage imageNamed:@"Hypno"];        // 将 UIImage 对象赋值给标签项的 iamge 属性    self.tabBarItem.image = i;  }  return self;}- (void)viewDidLoad {  [super viewDidLoad];    }- (void)loadView {  // 创建一个 JXHypnosisView 对象  JXHypnosisView * backgroundView = [[JXHypnosisView alloc] init];    CGRect textFieldRect = CGRectMake(40, 70, 240, 30);  UITextField * textField = [[UITextField alloc] init];  textField.frame = textFieldRect;    // 设置 UITextField 对象的边框样式,便于查看它在屏幕上的位置  textField.borderStyle = UITextBorderStyleRoundedRect;  // 修改占位符  textField.placeholder = @"Hypontize me";  // 修改键盘类型  textField.returnKeyType = UIReturnKeyDone;  textField.delegate = self;    [backgroundView addSubview:textField];    // 将 JXHypnosisView 对象赋给视图控制器的view 属性  self.view = backgroundView;}- (BOOL)textFieldShouldReturn:(UITextField *)textField {  NSLog(@"%@",textField.text);  return YES;}@end

  几乎所有的委托都是弱引用属性。这是为了避免对象及其委托之间产生强引用循环。例如: JXHypnosisViewController 是 UITextField 对象的委托,而且  UITextField 对象是  JXHypnosisViewController 的强引用属性,如果  UITextField 对象再对其委托保持强引用就会在两者之间产生强引用循环。

 

  • 向屏幕中添加UILabel对象

  下面我们在 JXHypnosisViewController 中添加一个新方法,在屏幕中随机位置绘制20个 UILabel 对象。同时,该方法有一个 NSString 类型的参数,表示 UILabel 对象显示的文字。

#import "JXHypnosisViewController.h"#import "JXHypnosisView.h"@interface JXHypnosisViewController ()<UITextFieldDelegate>@end@implementation JXHypnosisViewController- (instancetype)initWithNibName:(NSString *)nibNameOrNil             bundle:(NSBundle *)nibBundleOrNil {  self = [super initWithNibName:nibNameOrNil bundle:nibBundleOrNil];  if (self) {    // 设置标签项的标题    self.tabBarItem.title = @"Hypnotize";        // 从图片文件创建一个 UIImage 对象    UIImage * i = [UIImage imageNamed:@"Hypno"];        // 将 UIImage 对象赋值给标签项的 iamge 属性    self.tabBarItem.image = i;  }  return self;}- (void)viewDidLoad {  [super viewDidLoad];    }- (void)loadView {  // 创建一个 JXHypnosisView 对象  JXHypnosisView * backgroundView = [[JXHypnosisView alloc] init];    CGRect textFieldRect = CGRectMake(40, 70, 240, 30);  UITextField * textField = [[UITextField alloc] init];  textField.frame = textFieldRect;    // 设置 UITextField 对象的边框样式,便于查看它在屏幕上的位置  textField.borderStyle = UITextBorderStyleRoundedRect;  // 修改占位符  textField.placeholder = @"Hypontize me";  // 修改键盘类型  textField.returnKeyType = UIReturnKeyDone;  textField.delegate = self;    [backgroundView addSubview:textField];    // 将 JXHypnosisView 对象赋给视图控制器的view 属性  self.view = backgroundView;}- (BOOL)textFieldShouldReturn:(UITextField *)textField {  NSLog(@"%@",textField.text);  return YES;}- (void)drawHypnoticMessage:(NSString *)message {  for (NSInteger i = 0; i < 20; i++) {    UILabel * messageLabel = [[UILabel alloc] init];        // 设置 UIlabel 对象的文字和颜色    messageLabel.backgroundColor = [UIColor clearColor];    messageLabel.textColor = [UIColor whiteColor];    messageLabel.text = message;        //根据要显示的文字调整 UILabel 对象的大小    [messageLabel sizeToFit];        // 获取随机 x 坐标    // 使 UILabe 对象的宽度不超出控制器的 view 宽度    NSInteger width = self.view.bounds.size.width - messageLabel.bounds.size.width;    NSInteger x = arc4random() % width;        // 获取随机 y 坐标    // 使 UILabel 对象的高度不超出控制器的 view 宽度    NSInteger height = self.view.bounds.size.height - messageLabel.bounds.size.height;    NSInteger y = arc4random() % height;        // 设置 UILabel 对象的 frame    CGRect frame = messageLabel.frame;    frame.origin = CGPointMake(x, y);    messageLabel.frame = frame;        // 将 UILabel 对象添加到控制器的 view 中    [self.view addSubview:messageLabel];  }}@end

  接下来修改  textFieldShouldReturn: 将 UITextField 对象的文本内容作为 参数,调动我们自定义的方法,之后清空文本内容,最后调用 resignFirstResponder 关闭键盘。

#import "JXHypnosisViewController.h"#import "JXHypnosisView.h"@interface JXHypnosisViewController ()<UITextFieldDelegate>@end@implementation JXHypnosisViewController- (instancetype)initWithNibName:(NSString *)nibNameOrNil             bundle:(NSBundle *)nibBundleOrNil {  self = [super initWithNibName:nibNameOrNil bundle:nibBundleOrNil];  if (self) {    // 设置标签项的标题    self.tabBarItem.title = @"Hypnotize";        // 从图片文件创建一个 UIImage 对象    UIImage * i = [UIImage imageNamed:@"Hypno"];        // 将 UIImage 对象赋值给标签项的 iamge 属性    self.tabBarItem.image = i;  }  return self;}- (void)viewDidLoad {  [super viewDidLoad];    }- (void)loadView {  // 创建一个 JXHypnosisView 对象  JXHypnosisView * backgroundView = [[JXHypnosisView alloc] init];    CGRect textFieldRect = CGRectMake(40, 70, 240, 30);  UITextField * textField = [[UITextField alloc] init];  textField.frame = textFieldRect;    // 设置 UITextField 对象的边框样式,便于查看它在屏幕上的位置  textField.borderStyle = UITextBorderStyleRoundedRect;  // 修改占位符  textField.placeholder = @"Hypontize me";  // 修改键盘类型  textField.returnKeyType = UIReturnKeyDone;  textField.delegate = self;    [backgroundView addSubview:textField];    // 将 JXHypnosisView 对象赋给视图控制器的view 属性  self.view = backgroundView;}- (BOOL)textFieldShouldReturn:(UITextField *)textField {  NSLog(@"%@",textField.text);  [self drawHypnoticMessage:textField.text];  textField.text = @"";  [textField resignFirstResponder];  return YES;}- (void)drawHypnoticMessage:(NSString *)message {  for (NSInteger i = 0; i < 20; i++) {    UILabel * messageLabel = [[UILabel alloc] init];        // 设置 UIlabel 对象的文字和颜色    messageLabel.backgroundColor = [UIColor clearColor];    messageLabel.textColor = [UIColor whiteColor];    messageLabel.text = message;        //根据要显示的文字调整 UILabel 对象的大小    [messageLabel sizeToFit];        // 获取随机 x 坐标    // 使 UILabe 对象的宽度不超出控制器的 view 宽度    NSInteger width = self.view.bounds.size.width - messageLabel.bounds.size.width;    NSInteger x = arc4random() % width;        // 获取随机 y 坐标    // 使 UILabel 对象的高度不超出控制器的 view 宽度    NSInteger height = self.view.bounds.size.height - messageLabel.bounds.size.height;    NSInteger y = arc4random() % height;        // 设置 UILabel 对象的 frame    CGRect frame = messageLabel.frame;    frame.origin = CGPointMake(x, y);    messageLabel.frame = frame;        // 将 UILabel 对象添加到控制器的 view 中    [self.view addSubview:messageLabel];  }}@end

  构建并运行。

 

  • 运动效果

  iOS设备内嵌了许多功能强大的传感器,例如加速传感器,磁场传感器和三轴陀螺仪等。应用可以通过这些传感器了解设备加速,方向和角度,并实现有用的功能。例如,应用可以根据设备的方向自动将界面调整为横屏或者竖屏模式。从iOS7开始,苹果引用了一些新 API 可以轻松为应用添加一种通过传感器实现的视觉差效果。

  我们可以想象自己坐在一辆飞驰的大奔中,我们看想窗外,会发现远处的景物的倒退速度比近处的慢很多。这是大脑对空间和速度差异产生的一种错觉,称之为视差。在iOS7 及之后这种效果随处可见,例如,在主屏幕中,如果稍微倾斜设备,可以发现主屏幕中的图标会随着倾斜方向相对于壁纸移动。

  应用可以通过  UIInterpolatingMotionEffect 类来实现相同的效果,我们只需要创建一个  UIInterpolatingMotionEffect 对象,设置其方向(垂直或者水平)、键路劲(key path,需要使用视差效果的属性)和相对最小/最大值(视觉差的范围),再将其添加到某个视图上,该视图就能获得相应的视差效果。

#import "JXHypnosisViewController.h"#import "JXHypnosisView.h"@interface JXHypnosisViewController ()<UITextFieldDelegate>@end@implementation JXHypnosisViewController- (instancetype)initWithNibName:(NSString *)nibNameOrNil             bundle:(NSBundle *)nibBundleOrNil {  self = [super initWithNibName:nibNameOrNil bundle:nibBundleOrNil];  if (self) {    // 设置标签项的标题    self.tabBarItem.title = @"Hypnotize";        // 从图片文件创建一个 UIImage 对象    UIImage * i = [UIImage imageNamed:@"Hypno"];        // 将 UIImage 对象赋值给标签项的 iamge 属性    self.tabBarItem.image = i;  }  return self;}- (void)viewDidLoad {  [super viewDidLoad];    }- (void)loadView {  // 创建一个 JXHypnosisView 对象  JXHypnosisView * backgroundView = [[JXHypnosisView alloc] init];    CGRect textFieldRect = CGRectMake(40, 70, 240, 30);  UITextField * textField = [[UITextField alloc] init];  textField.frame = textFieldRect;    // 设置 UITextField 对象的边框样式,便于查看它在屏幕上的位置  textField.borderStyle = UITextBorderStyleRoundedRect;  // 修改占位符  textField.placeholder = @"Hypontize me";  // 修改键盘类型  textField.returnKeyType = UIReturnKeyDone;  textField.delegate = self;    [backgroundView addSubview:textField];    // 将 JXHypnosisView 对象赋给视图控制器的view 属性  self.view = backgroundView;}- (BOOL)textFieldShouldReturn:(UITextField *)textField {  [self drawHypnoticMessage:textField.text];  textField.text = @"";  [textField resignFirstResponder];  return YES;}- (void)drawHypnoticMessage:(NSString *)message {  for (NSInteger i = 0; i < 20; i++) {    UILabel * messageLabel = [[UILabel alloc] init];        // 设置 UIlabel 对象的文字和颜色    messageLabel.backgroundColor = [UIColor clearColor];    messageLabel.textColor = [UIColor whiteColor];    messageLabel.text = message;        //根据要显示的文字调整 UILabel 对象的大小    [messageLabel sizeToFit];        // 获取随机 x 坐标    // 使 UILabe 对象的宽度不超出控制器的 view 宽度    NSInteger width = self.view.bounds.size.width - messageLabel.bounds.size.width;    NSInteger x = arc4random() % width;        // 获取随机 y 坐标    // 使 UILabel 对象的高度不超出控制器的 view 宽度    NSInteger height = self.view.bounds.size.height - messageLabel.bounds.size.height;    NSInteger y = arc4random() % height;        // 设置 UILabel 对象的 frame    CGRect frame = messageLabel.frame;    frame.origin = CGPointMake(x, y);    messageLabel.frame = frame;        // 将 UILabel 对象添加到控制器的 view 中    [self.view addSubview:messageLabel];         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];  }}@end

  测试运动效果必须在真机上。

 

  • 深入学习:main() 和 UIApplication

  用C语言编写的程序,其至此那个入口都是 main() 。用 Objective-C 语言编写的程序也是这样。

  在  JXHypnoNerd 项目中我们在 mian.m,可以看到有如下代码

#import <UIKit/UIKit.h>#import "AppDelegate.h"int main(int argc, char * argv[]) {  @autoreleasepool {    return UIApplicationMain(argc, argv, nil, NSStringFromClass([AppDelegate class]));  }}

  这段代码中的  UIApplicationMain  函数会创建一个 UIApplication 对象。 每个iOS应用都有且只有一个UIApplication对象,该对象的作用就是维护运行循环。一旦程序创建了某个 UIApplication 对象,该对象的运行循环就会一直循环下去,main() 的执行也会因此堵塞。

  此外, UIApplicationMain 函数还会创建某个指定类的对象,并将其设置为 UIApplication 对象的 delegate 。该对象的类是由 UIApplicationMain 函数的最后一个实参指定的,该实参的类型是 NSString 对象,代表的是某个类的类名。所以在以上这段代码中, UIApplicationMain 会创建一个 AppDelegate 对象,并将其设置为 UIApplication 对象的 delegate

  在应用启动运行循环并开始接收事件之前,UIApplication 对象会向其委托发送一个特性的消息,使应用能有机会完成相应的初始化工作。这个消息的名称是

- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions