《iOS创意程序设计家》——第6.4节事件检测

    xiaoxiao2023-12-15  149

    本节书摘来自异步社区《iOS创意程序设计家》一书中的第6章,第6.4节事件检测,作者 林柏全,更多章节内容可以访问云栖社区“异步社区”公众号查看

    6.4 事件检测iOS创意程序设计家界面控制器除了负责界面的管理以及布局外,还负责事件的传递。这些事件包括我们在第5章已经介绍过的触控事件,还有接下来要介绍的晃动检测事件。这些事件都定义在UIResponder类里面,而无论是界面控制器UIViewController还是界面UIView,它们都继承自UIResponder类。

    6.4.1 晃动检测首先,我们来看看晃动事件的处理。与触控事件类似的是,晃动检测也是由一连串的事件所组成的,不过,要让您的应用程序支持晃动检测,必须让您的界面控制器成为First Responder才行。要让某个界面控制器成为First Responder,除了调用becomeFirstResponder这个方法外,还需要改写canBecomeFirstResponder这个方法,并返回YES,使得这个界面控制器可以成为First Responder,使用代码如下所示:

    -(void) viewDidAppear:(BOOL) animated {   [super viewDidAppear:animated];   [self becomeFirstResponder]; } -(void) viewDidDisappear:(BOOL) animated {   [self resignFirstResponder];   [super viewDidDisappear:animated]; } -(BOOL) canBecomeFirstResponder {   Return YES; } -(void)motionBegan:(UIEventSubtype)motion withEvent:(UIEvent *)event {     if( motion==UIEventSubtypeMotionShake)       NSLog(@"开始摇晃"); } -(void)motionCancelled:(UIEventSubtype)motion withEvent:(UIEvent *)event {   if( motion==UIEventSubtypeMotionShake)       NSLog(@"取消摇晃"); } -(void)motionEnded:(UIEventSubtype)motion withEvent:(UIEvent *)event {   if( motion==UIEventSubtypeMotionShake)       NSLog(@"结束摇晃"); }

    摇晃您的手机看看UIViewController是否可以接收到这些事件。

    6.4.2 应用范例:魔术秀有了以上的摇晃事件的说明后,我们可以很容易地运用这个事件来做一些应用。例如,我们可以将摇晃事件运用于魔术上。当然,这个魔术只需要有一点点的表演天分,而不需要有任何魔术的技巧。这个魔术的表演过程是这样的,首先让任何一位观众看看手机上出现的3张牌,当然,由系统随机出现的这3张牌都是A的机率是非常小的。为了取信观众,您甚至可以让观众任意触摸界面上的这3张牌。保持手部不触控到屏幕界面,然后在观众面前用力往下一甩,您会发现界面上的3张牌全部变成了A,再甩一次,界面中的3张牌又会恢复到观众原先看到的那3张牌了。通过以下的步骤,我们就可以实现这个魔术秀的应用程序。

    学习重点:

    Outlet Collection的使用;晃动检测;随机数的使用;动画的使用。

    请记得勾选“Use Storyboard”以及“Use Automatic Reference Counting”选项,并将扑克牌等图形文件加入到项目内。

    打开MainStoryboard.storyboard,并在界面上加入3个UIImageView的控件。然后指定这3张图形为扑克牌的背面图cover.png。

    请注意,这3张扑克牌的顺序会因为加入UIImageView的顺序不同而有所不同,先加入的控件会放在下面,如图6.14所示。如果有需要的话,也可以通过XCode选单上的“Editor”→“Arrange”来调整前后位置。

    接下来,我们得将扑克牌连接为Outlet。与以往不同的是,我们要将这3种牌连接为Outlet Collection以方便后续的处理。连接的方式与Outlet一样,首先,将第1张牌连接为一个Outlet Collection,并命名为“poker”,接着分别将第2张与第3张连接到相同的Outlet Collection中就可以了。现在ViewController.m看起来应该如下所示:

    #import <UIKit/UIKit.h> @interface ViewController : UIViewController   @property (strong, nonatomic) IBOutletCollection(UIImageView) NSArray *pokers; @end

    我们需要两个变量来记录发牌的状态,并且要定义一个发牌方式的自定义变量形态。其中,isCheat用来记录目前是否是3张A状态,而oldPokers则用来记录原先发的牌以方便后续我们可以还原回去。

    Ch6\MagicShow\ MagicShow\ViewController.h #import <UIKit/UIKit.h> // 定义扑克牌发牌方式(作弊、不作弊,以及恢复原有的牌) typedef enum {cheat,no_cheat,restore} DealOption; @interface ViewController : UIViewController {     // 用来记录目前3张牌是否均为A   BOOL isCheat;   // 用来记录原先发的3张牌   int oldPokers[3]; } @property (strong, nonatomic) IBOutletCollection(UIImageView) NSArray *pokers; // 发新的扑克牌 - (void) dealPokersWithOption: (DealOption) option; @end

    现在为魔术秀加入摇晃事件的支持。要让一个界面控制器支持摇晃事件的第一步就是,先让这个界面控制器可以成为First Responder。因此,我们要在ViewController.m里面加入以下的方法:

    - (BOOL) canBecomeFirstResponder {   return YES; } 第二步则是让ViewController成为First Responder,并在不需要的时候让它退出First Responder。因此,我们需要在界面出现的时候调用becomeFirstResponder,以及在界面消失的时候调用resignFirstResponder。 // 使应用程序支持Shake - (void) viewDidAppear:(BOOL)animated {   [super viewDidAppear:animated];   [self becomeFirstResponder]; } - (void) viewDidDisappear:(BOOL)animated {   [super viewDidDisappear:animated];   [self resignFirstResponder]; }

    接下来,我们可以去处理初始发牌以及摇晃后换牌的程序代码。在以下的程序代码中,我们通过isCheat来判断目前是否需要更换为3张A的状态,如果需要的话,就通过dealPokersWithOption:cheat来更换,否则就通过dealPokersWithOption :restore还原回去,使用代码如下:

    // 初始发牌 - (void)viewDidLoad {   [super viewDidLoad];    // 一开始不要变3张A   [self dealPokersWithOption:no_cheat]; } // 晃动检测 - (void)motionEnded:(UIEventSubtype)motion withEvent:(UIEvent *)even {      isCheat = !isCheat;      if( isCheat )        [self dealPokersWithOption:cheat];      else        [self dealPokersWithOption:restore];  } 6.jpg 接下来是发牌方法的实现。 打开ViewController.m并加入以下内容: Ch6\MagicShow\ MagicShow\ViewController.m // 发牌 - (void) dealPokersWithOption: (DealOption) option {    int value = 1; // 1表示A    int i=0;      for (UIImageView *imageView in pokers) {      if (option==cheat) {        value = 1;      }else if( option==no_cheat ){        if( !cheat ){          value = (arc4random() % 13) + 1; // 得到1~13之间的数字          oldPokers[i++] = value;        }      }else{        value = oldPokers[i++];      }        [UIView transitionWithView:imageView           duration:1.5f options:UIViewAnimationOptionCurveEaseInOut|UIView- AnimationOptionTransitionFlipFromLeft           animations:^(void){             imageView.image = [UIImage imageNamed:[NSString                        stringWithFormat:@"%d.png",value]];            } completion:nil];    } }

    上述的程序代码通过“(arc4random() % 13) + 1”这一行程序代码来取得随机数所决定的扑克牌号码。通过这个方式便可以让随机数所决定的扑克牌号码落在1到13之间。整个魔术秀的应用程序就是这么简单,最后,让我们看一下魔术秀的表演,如图6.15所示。

    6.4.3 加速度计加速度计(accelerometer)在iPhone内扮演着很重要的角色,包括本节内容所介绍的摇晃事件其实也是加速度计的一个应用。

    如图6.16所示,iOS的加速度计是由3个轴向所构成的。也就是说,它可以测量3个方向的加速度值,而这个加速度值的单位就是大家在物理课所学到的重力加速度(1G=9.81 m/s2)。以图6.16来说,当手机往右手边旋转时,这时候x轴的加速度值便会越来越大;相反地,则x轴的加速度值便会越来越小。

    有了以上的概念后,我们来看看如何在应用程序内加入加速度的检测。

    首先,必须在类的定义中加入UIAccelerometerDelegate协议,例如:

    @interface MyViewController : UIViewController<UIAccelerometerDelegate> 接下来,必须指定加速度计的代理者为MyViewController这个程序,并通过updateInterval属性来指定加速度计数值更新的频率。例如: UIAccelerometer *accel = [UIAccelerometer sharedAccelerometer]; accel.delegate = self; accel.updateInterval = 1.0f / 60.0f;

    最后则是加入加速度数值改变后的通知事件,使用代码如下:

    - (void)accelerometer:(UIAccelerometer *)accelerometer    didAccelerate:(UIAcceleration *)acceleration { }

    在这个事件内所传入的acceleration参数便带有x、y、z三个轴向的数值。例如:

    acceleration.x acceleration.y acceleration.z

    6.4.4 应用范例:水平仪由于加速度计本身的特性,我们可以利用得到的3个轴向的加速度值来做一些有趣的应用,例如,把iOS设备当作水平仪来使用。水平仪是一种用来检验一个物品是否呈现水平状态的简单仪器。常见的水平仪是气泡式水平仪,这种水平仪里面注满了液体,但仪器中央会有一个小气泡。该气泡会随着待测的平面高度的变化而改变位置,若待测平面呈现水平状态,则气泡会位于仪器的正中央。对水平仪有了简单的了解之后,我们便可以把iOS设备变成水平仪了。

    学习重点:

    加速度计的使用;如何让手机固定一个方向;使用setTransform:来移动对象。

    在建立项目过程中,请记得勾选“Use Storyboard”以及“Use Automatic Reference Counting”选项。

    由于在使用水平仪的时候不希望界面随着用户的转动而改变方向,我们要去设置该应用程序只会固定在Landscape Left方向。请在Targets设置中的Info界面加入以下两个设置,如图6.17所示。

    其中,Supported interface orientations用来设置这部设备可以支持的方向,而Initial interface orientation则用来设置默认的方向。

    最后,我们要到ViewController.m中来取消自动转向的设置,代码如下:

    -(BOOL)shouldAutorotateToInterfaceOrientation: (UIInterfaceOrientation)interfaceOrientation {   return (interfaceOrientation == UIInterfaceOrientationLandscapeLeft); ; }

    这样一来,iOS设备就完全不会自动转向了。现在执行一下应用程序,看看方向是否正确。

    直接将水平仪与气泡的图形拉到项目中,并选择“Copy items into destination group's folder”以复制文件到项目中。

    打开MainStoryboard.storyboard,会发现手机的界面已经变成横向显示了。现在从控件库中拉进两个Image View到界面中,其中一个为水平仪本身的图形,另外一个则是气泡的图形。

    为了精确定位,我们可以切换到尺寸观测窗口(size inspector),并分别设置这两个Image View的位置与大小,例如,

    水平仪:(240,160)高度65

    气泡:(240,160)宽度与高度均为30

    接下来就可以在属性观测窗口(attribute inspector)分别为这两个控件设置图形了。完成后的界面应该如图6.18所示。

    由于我们需要变动气泡的位置,因此,必须要将气泡先连接为Outlet以方便控制。选择气泡的图形,并打开编辑器的辅助模式,按住“Control”键不放,然后连接为bubble。

    打开ViewController.h并在类定义的后面加上UIAccelerometerDelegate协议。ViewController.h应该如下所示:

    #import <UIKit/UIKit.h> @interface ViewController : UIViewController <UIAccelerometerDelegate> @property (strong, nonatomic) IBOutlet UIImageView *bubble; @end 7.jpg加入加速度计的检测。 打开ViewController.m并修改如下: 首先在viewDidLoad指定加速度计的代理者为“ViewController”。 - (void)viewDidLoad {    [super viewDidLoad];    UIAccelerometer *accel = [UIAccelerometer sharedAccelerometer];    accel.delegate = self;    accel.updateInterval = 1/60; } 接着要编写加速度计的事件处理,代码如下: // 当加速度计的数值改变时,同时也改变气泡的位置 - (void) accelerometer:(UIAccelerometer *)accelerometer    didAccelerate:(UIAcceleration *)acceleration {    [bubble setTransform:      CGAffineTransformMakeTranslation(-acceleration.y*20,-acceleration.x*10)]; }

    在上述的程序代码里面,我们通过UIView的setTransform:方法来改变气泡的位置,由于x、y轴的正负值刚好与气泡运动的位置相反,因此要乘以-1来改变。读者们可以自行验证这一点。程序最终的执行界面如图6.19所示。

    最新回复(0)