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

简介:

本节书摘来自异步社区《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”来调整前后位置。


235b7c00ab8554c5933ff9d82d75358d26d1dbec

接下来,我们得将扑克牌连接为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所示。


e6b7474cb13198e4e8c928a2e31b03f8fbd89ea7

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

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


12af0217e4635925c831b6ccb27ff0ac8c34b2e2

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

首先,必须在类的定义中加入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所示。


f57e8bfab37ebe2b1129f45809b55579dccd3133

其中,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所示。


0e8b7223afd9a49ea666c4d79ffd2c0e3c53aaf9

由于我们需要变动气泡的位置,因此,必须要将气泡先连接为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所示。


1f119906c1af0f2b3a6f5b16cca1c30457461ded
相关文章
|
测试技术 程序员 C++
iOS:项目中无用类检测和无用图片检测汇总
在涉及到项目大改版,或者涉及到某个功能模块大变更,就会涉及到图片废弃和文件废弃的情况。 但是这时候就会遗留下一个很大的问题,没有将废弃的、无用的文件类或资源删除干净。而这次需要对工程代码的无用资源和无用文件进行删除处理,感触颇多,故在此笔记。 首先,感觉很多人的代码习惯还是恶待提高。比如我发现一些人的代码操作习惯,从好到次,可以大略分以下情况
1144 0
iOS:项目中无用类检测和无用图片检测汇总
|
1天前
|
监控 API iOS开发
克魔助手 - iOS性能检测平台
众所周知,如今的用户变得越来越关心app的体验,开发者必须关注应用性能所带来的用户流失问题。目前危害较大的性能问题主要有:闪退、卡顿、发热、耗电快、网络劫持等,但是做过iOS开发的人都知道,在开发过程中我们没有一个很直观的工具可以实时的知道开发者写出来的代码会不会造成性能问题,虽然Xcode里提供了耗电量检测、内存泄漏检测等工具,但是这些工具使用效果并不理想(如Leak无法发现循环引用造成的内存泄漏)。所以这篇文章主要是介绍一款实时监控app各项性能指标的工具,包括CPU占用率、内存使用量、内存泄漏、FPS、卡顿检测,并且会分析造成这些性能问题的原因。
|
8月前
|
iOS开发
iOS UIDevice & 屏幕旋转检测
iOS UIDevice & 屏幕旋转检测
31 0
|
10月前
|
存储 iOS开发
iOS主线程耗时检测方案
找出那个拖后腿的凶手
144 1
iOS主线程耗时检测方案
|
12月前
|
iOS开发
iOS 检测字符串中数字个数、特殊符号个数
iOS 检测字符串中数字个数、特殊符号个数
141 0
|
12月前
|
iOS开发
iOS 检测字符串中是否含有数字、特殊符号
iOS 检测字符串中是否含有数字、特殊符号
320 0
|
12月前
|
iOS开发
iOS 检测字符串中是否含有字母、大写字母、小写字母
iOS 检测字符串中是否含有字母、大写字母、小写字母
287 0
|
iOS开发
iOS开发 - touchBegan事件判断点击的位置在View上还是在View的子View上
iOS开发 - touchBegan事件判断点击的位置在View上还是在View的子View上
240 0
iOS开发 - touchBegan事件判断点击的位置在View上还是在View的子View上
|
iOS开发
iOS开发- runtime基本用法解析和用runtime给键盘添加工具栏和按钮响应事件
iOS开发- runtime基本用法解析和用runtime给键盘添加工具栏和按钮响应事件
118 0
|
iOS开发
IOS检测版本更新(***为app id)
IOS检测版本更新(***为app id)
56 0