精华内容
下载资源
问答
  • 文章目录事件传统方式addEventListener常见的事件对象得属性和方法返回触发事件对象e.target 返回的是触发事件对象(元素)this 返回的是绑定事件对象(元素)扩展 this 有个非常相似的属性 currentTarget 在IE678...

    事件

    传统方式

    <div>123</div>
    
    var div = document.querySelector('div')
          div.onclick = function (event) {
            console.log(event)
          }
    

    addEventListener

    div.addEventListener('click', function (event) {
            console.log(event)
          })
    

    事件对象也是兼容性问题 IE678 通过winndow.event

    div.onclick = function () {
            console.log(window.event) //IE678只能识别这种写法
          }
    

    为了解决兼容性:

    div.onclick = function (e) {
            e = e || window.event
            console.log(e) //这是兼容性写法
          }
    

    1.event就是一个事件对象 写到我们的侦听函数得 小括号里面 当形参来看
    2.事件对象只有有了事件才存在 他是系统给我们自动创建得,不需要为我们传递参数
    3.事件对象 是 我们事件得一系列相关的数据得集合 跟事件相关的 比如鼠标点击里面就包含了鼠标的相关信息,鼠标坐标等,如果是键盘事件里面就包含了键盘事件,比如判断用户按下了哪个键位都知道
    4.这个事件对象我们可以自己命名 比如 even、evt、e

    常见的事件对象得属性和方法

    返回触发事件的对象

    e.target 返回的是触发事件得对象(元素)

    <div>123<div>
    
    	var div = document.querySelector('div')
        div.addEventListener('click', function (e) {
          console.log(e.target) //返回的<div>123</div>
        })
    

    而有一个相同的this

    this 返回的是绑定事件得对象(元素)

    <ul>
    	<li>123</li>
    	<li>123</li>
    	<li>132</li>
    </ul>
    
    var ul = document.querySelector('ul')
          ul.addEventListener('click', function () {
            //我们给别 绑定了事件 那么this 就指向ul
            console.log(this) //返回的是<ul><li>abc</li><li>abc</li><li>abc</li></ul>
          })
    

    两者区别:
    e.target 指向我们点击的对象 谁触发了这个事件 我们点击的是li e.target 指向的就是li
    我们给ul 绑定了事件 那么this 就指向ul

    扩展 this 有个非常相似的属性 currentTarget 在IE678兼容

    	ul.addEventListener('click', function (e) {
            console.log(e.currentTarget)//返回的是<ul><li>abc</li><li>abc</li><li>abc</li></ul>
          })
    

    因为e.target IE678 不支持所以我们使用 e.srcElement 不标准IE678支持

    为了使用所以我们使用兼容性写法
    div.onclick = function () {
            e = e || window.event
            var target = e.target || e.srcElement
            console.log(target)
          }
    

    返回事件类型

    e.type返回事件类型

    <div>123</div>
    
    var div = document.querySelector('div')
          //e.type返回事件类型 比如 click mouseover 不带on
          div.addEventListener('click', fn)
          div.addEventListener('mouseover', fn)
          div.addEventListener('mouseout', fn)
          function fn(e) {
            console.log(e.type) //得到相应的事件类型
          }
    

    阻止默认行为(事件) 让链接不跳转 或者让提交按钮不提交

    e.preventDefault()

    <a href="http://www.baidu.com">百度</a>
    
    var a = document.querySelector('a')
          a.addEventListener('click', function (e) {
            e.preventDefault() //  dom 标准写法
          })
    

    在这里有一个需要注意 element.addEventListener 只支持 e.preventDefault()

    传统的注册方式
    <a href="http://www.baidu.com">百度</a>
    
    • 普通浏览器 e.preventDefault(); 方法 不考虑兼容性可用
    a.onclick = function (e) {
             e.preventDefault()
          }
    
    • 低版本浏览器 ie678 returnValue 属性
    a.onclick = function (e) {
             e.returnValue
          }
    
    • 我们可以利用return false 也能阻止默认行为 没有兼容性问题
      特点: return 后面的代码不执行了, 而且只限于传统的注册方式 addEventListener不支持
    a.onclick = function (e) {
            return false
            alert(11)
          }
    

    阻止冒泡

    • e.stopPropagation()

    css是我们更方便辨认从上倒下的层次关系

    .father {
            overflow: hidden;
            width: 300px;
            height: 300px;
            margin: 100px auto;
            background-color: pink;
            text-align: center;
          }
    
          .son {
            width: 200px;
            height: 200px;
            margin: 50px;
            background-color: purple;
            line-height: 200px;
            color: #fff;
          }
    

    上次我们提到,他的层次由 document -> html -> body -> father -> son

    <div class="father">
          <div class="son">son儿子</div>
        </div>
    

    在这里我们输出 document -> father -> son 作为示范

     var son = document.querySelector('.son')
          // e.stopPropagation()要阻止哪个冒泡就放在哪个函数里面
          son.addEventListener(
            'click',
            function (e) {
              alert('son')
            },
            false
          )
    var father = document.querySelector('.father')
          father.addEventListener(
            'click',
            function () {
              alert('father')
            },
            false
          )
          document.addEventListener('click', function () {
            alert('document')
          })
    

    输出得结果为点击son块时,分别弹出son -> father -> document

    当我们在son函数添加e.stopPropagation()时:

    var son = document.querySelector('.son')
          son.addEventListener(
            'click',
            function (e) {
              alert('son')
              e.stopPropagation() // stop 停止  Propagation 传播
            },
            false
          )
          var father = document.querySelector('.father')
          father.addEventListener(
            'click',
            function () {
              alert('father')
            },
            false
          )
          document.addEventListener('click', function () {
            alert('document')
          })
    

    注意:e.stopPropagation()要阻止哪个冒泡就放在哪个函数里面
    写在son函数里面,点击father时不能阻止冒泡

    注意:e.stopPropagation()虽然很方便,但是IE6、7、8并不支持,我们在IE低版本只有通过window.event.cancelBubble = true来实现阻止冒泡

    var son = document.querySelector('.son')
          son.addEventListener(
            'click',
            function (e) {
              alert('son')
              e.cancelBubble = true // 非标准 cancel 取消 bubble 泡泡
            },
            false
          )
          var father = document.querySelector('.father')
          father.addEventListener(
            'click',
            function () {
              alert('father')
            },
            false
          )
          document.addEventListener('click', function () {
            alert('document')
          })
    

    为了解决兼容性问题,所以我们用兼容性的写法

    	if (e && e.stopPropagation) {
            e.stopPropagation()
          } else {
            window.event.cancelBubble = true
          }
    

    事件委托

    事件委托的核心原理:给父节点添加侦听器, 利用事件冒泡影响每一个子节点

    <ul>
          <li>知否知否,点我应有弹框在手!</li>
          <li>知否知否,点我应有弹框在手!</li>
          <li>知否知否,点我应有弹框在手!</li>
          <li>知否知否,点我应有弹框在手!</li>
          <li>知否知否,点我应有弹框在手!</li>
        </ul>
    
    	var ul = document.querySelector('ul')
          var flag = 0
          ul.addEventListener('click', function (e) 			 {
            // alert('知否知否,点我应有弹框在手!')
            // e.target //这个可以得到我们点击的对象
            e.target.style.backgroundColor = 'pink'
          })
    

    鼠标事件

    禁用右键

    选中文字无法右击出菜单

    //1.contextmenu禁用右击菜单
          document.addEventListener('contextmenu', function (e) {
            e.preventDefault()
          })
    

    禁止鼠标选中

    document.addEventListener('selectstart', function (e) {
            e.preventDefault()
          })
    

    扩展 禁用F12键

    	document.onkeydown = document.onkeyup = document.onkeypress = function (
            event
          ) {
            var e = event || window.event || arguments.callee.caller.arguments[0]
    
            if (e && e.keyCode == 123) {
              mAlert()
              e.returnValue = false
              return false
            }
          }
          function mAlert() {
            alert('禁止操作控制台')
          }
          document.onmousedown = function mdClick(event) {
            var e = event || window.event || arguments.callee.caller.arguments[0]
            if (e.button == 2 || e.button == 3) {
              mAlert()
            }
          }
    

    鼠标事件对象

    e.clientX 和 e.clientY 只获取可视区 就算文章很长有滚动条得到的也只是可视区的坐标

    	document.addEventListener('click', function (e) {
            console.log(e.clientX) //距离左边距距离坐标
            console.log(e.clientY) //距离上边距距离坐标
        })
    

    e.pageX 和 e.pageY 获取整个文档 e.pageY随着文档长度而变化 此方法有兼容性问题 ie9以上才支持

    body {
            height: 3000px;
          }
    
    document.addEventListener('click', function (e) {
            console.log(e.pageX) //距离整个页面大的左边距距离坐标
            console.log(e.pageY) //距离整个页面大的上边距距离坐标
          })
    

    e.screenX 和 e.screenY是得到整个屏幕坐标

    body {
            height: 3000px;
          }
    
    document.addEventListener('click', function (e) {
            console.log(e.screenX) //距离电脑屏幕的左边距距离坐标
            console.log(e.screenY) //距离电脑屏幕的上边距距离坐标
          })
    

    下为图解
    在这里插入图片描述

    鼠标事件案例

    mousemove只要我们鼠标移动1px 就会触发这个事件

    • 因为我们是在页面移动,所以我们给document注册事件
    • 图片·移动距离不占用位置,使用绝对定位
    • 核心原理: 每次鼠标移动,我们都会获得最新的鼠标坐标, 把这个x和y坐标做为图片的top和left 值就可以移动图片
    <style>
          img {
            position: absolute;
            top: 2px;
          }
        </style>
    
    	<img src="images/angel.gif" alt="" />
        <script>
          var pic = document.querySelector('img')
          document.addEventListener('mousemove', function (e) {
            // 1. mousemove只要我们鼠标移动1px 就会触发这个事件
            // console.log(1);
            // 2.核心原理: 每次鼠标移动,我们都会获得最新的鼠标坐标, 把这个x和y坐标做为图片的top和left 值就可以移动图片
            var x = e.pageX
            var y = e.pageY
            console.log('x坐标是' + x, 'y坐标是' + y)
            //3 . 千万不要忘记给left 和top 添加px 单位
            pic.style.left = x - 50 + 'px'
            pic.style.top = y - 40 + 'px'
          })
        </script>
    

    常见的键盘事件

    onkeyup 按键弹起的时候 触发
    onkeydown 按键按下的时候 触发
    keypress 按键按下的时候 触发 注意 他不识别功能键 比如 ctrl shift 左右键等

    //常见的键盘事件
          // document.onkeyup = function () {
          //   console.log('我弹起了')
          // }
          document.addEventListener('keyup', function () {
            console.log('我弹起了')
          })
          document.addEventListener('keydown', function () {
            console.log('我按下了')
          })
          document.addEventListener('keypress', function () {
            console.log('我按下了press')
          })
          
    

    注意 : 优先级 keydown > keypress 左后执行keyup

    键盘对象

    键盘事件对象keyCode属性可以i得到相应的ASCII码值
    我们的 keyupkeydown 事件不区分字母大小写 a 和 A 得到的都是65
    我们的 keypress 虽然不知别功能键 但是区分字母大小写 返回不同的

    <script>
          ASCII码
          document.addEventListener('keyup', function (e) {
            console.log('keyup:' + e.keyCode)
            if (e.keyCode === 65) {
              alert('您按下了a键')
            } else {
              alert('您没有按下了a键')
            }
          })
          // 我们可以利用keyCode返回的ASCII值确定我们按的哪个值
          document.addEventListener('keypress', function (e) {
            console.log('keypress:' + e.keyCode)
          })
        </script>
    

    ASCII表

    在这里插入图片描述

    键盘对象案例
    模拟京东按键快速定位搜索框
    <input type="text" />
        <script>
          // 核心思路: 检测用户是否按下了s 键,如果按下s 键,就把光标定位到搜索框里面
          // 使用键盘事件对象里面的keyCode 判断用户按下的是否是s键
          // 搜索框获得焦点: 使用 js 里面的 focus() 方法
          var search = document.querySelector('input')
          // document.addEventListener('keydown', function (e) {
          //   // console.log(e.keyCode);
          //   if (e.keyCode === 83) {
          //     search.focus()
          //   }
          // })
          //使用keydown时会在按下同时执行所以会输出s
          document.addEventListener('keyup', function (e) {
            // console.log(e.keyCode);
            if (e.keyCode === 83) {
              search.focus()
            }
          })
        </script>
    
    键盘事件-模拟京东快递单号查询案例
    <body>
        <div class="search">
          <div class="con">123</div>
          <input type="text" placeholder="请输入您的快递单号" class="jd" />
        </div>
      </body>
    
    
    ```html
    
    ```css
    <style>
          * {
            margin: 0;
            padding: 0;
          }
    
          .search {
            position: relative;
            width: 178px;
            margin: 100px;
          }
    
          .con {
            display: none;
            position: absolute;
            top: -40px;
            width: 171px;
            border: 1px solid rgba(0, 0, 0, 0.2);
            box-shadow: 0 2px 4px rgba(0, 0, 0, 0.2);
            padding: 5px 0;
            font-size: 18px;
            line-height: 20px;
            color: #333;
          }
    
          .con::before {
            content: '';
            width: 0;
            height: 0;
            position: absolute;
            top: 28px;
            left: 18px;
            border: 8px solid #000;
            border-style: solid dashed dashed;
            border-color: #fff transparent transparent;
          }
        </style>
    
    
    
    <script>
        var con = document.querySelector('.con')
        var jd_input = document.querySelector('.jd')
        jd_input.addEventListener('keyup', function () {
          // console.log('123456')
          if (this.value == '') {
            con.style.display = 'none'
          } else {
            con.style.display = 'block'
            con.innerText = this.value
          }
        })
        //当我们失去焦点就影藏这个从盒子
        jd_input.addEventListener('blur', function () {
          con.style.display = 'none'
        })
        // 当我们获取焦点就影藏这个从盒子
        jd_input.addEventListener('focus', function () {
          if (this.value == '') {
            con.style.display = 'none'
          } else {
            con.style.display = 'block'
          }
        })
      </script>
    

    展开全文
  • 事件是什么 ...在VIsualBasice应用程序中,对象事件是由VisualBasic预先定义好的,能够被对象识别和响应的动作。 分类: 事件可分为系统事件和用户事件两种 1、系统事件是由系统或对象自己产生的...

    事件是什么

    事件是指对象能够识别并做出反应的外部刺激。如一个迟到的学生这个对象,当听到上课铃声这个事件,学生这个对象做出的反应,即对事件的处理过程就是向教室急跑。

    在VB中的对象事件是什么

    在VIsual Basice应用程序中,对象事件是由Visual Basic预先定义好的,能够被对象识别和响应的动作。

    分类:

    事件可分为系统事件和用户事件两种

    1、系统事件是由系统或对象自己产生的,如当一个窗体被加载到内存准备显示时, 发生的加载(Load)事件,“定时器时间到”事件等:

    2、用户事件是由用户操作引起的,如鼠标单击(Click)事件、双击(DbIClick) 事件、标移动(ouseMove事件等。

          不同的对象所能识别的事件是不同的,如窗体对象能识别加载(Load) 事件,而其他控件对象就不能识别这个事件。每个事件都有名称,即事件名。事件名也是Visual Basic系统的关键字,不要用作对象名、文件名、变量名等。

    事件驱动机制
     Visual Basic应用程序的运行过程就是对事件的处理过程。程序运行时,由用户、系统或对象产生各种不同的事件,程序设计者已分别为各种不同的事件编写了处理代码程序,窗体和控件等对象在响应不同事件时执行不同的代码程序,这就是Visual Basic事件驱动机制。

    事件过程

    当对窗体和控件对象产生如单击(Click)、 双击(DIClick)、 鼠标移动(MouseMove)等事件时,接受事件的对象就会对事件产生反应,即执行-段程序代码,所执行的这段程序代码就称为事件过程。

    事件过程是一个独立的程序段,是应用程序的重要组成部分。

    在设计一个工程中,当在工程中添加了窗体和控件等对象,并通过属性设置他们的初始属性值之后,就应该编写他们的事件过程。这样在程序运行过程中,各种对象才能对用户的操作做出响应,完成应用程序要实现的功能。



     

    展开全文
  • 概览 iPhone的成功很大一部分得益于它多点触摸的强大功能,乔布斯让...iOS事件简介触摸事件手势识别运动事件远程控制事件 iOS事件 在iOS中事件分为三类: 触摸事件:通过触摸、手势进行触发(例如手指点击、缩放)

    概览

    iPhone的成功很大一部分得益于它多点触摸的强大功能,乔布斯让人们认识到手机其实是可以不用按键和手写笔直接操作的,这不愧为一项伟大的设计。今天我们就针对iOS的触摸事件(手势操作)、运动事件、远程控制事件等展开学习:

    iOS事件

    在iOS中事件分为三类:

    1. 触摸事件:通过触摸、手势进行触发(例如手指点击、缩放) 
    2. 运动事件:通过加速器进行触发(例如手机晃动) 
    3. 远程控制事件:通过其他远程设备触发(例如耳机控制按钮)

    下图是苹果官方对于这三种事件的形象描述:

    events_to_app_2x

    在iOS中并不是所有的类都能处理接收并事件,只有继承自UIResponder类的对象才能处理事件(如我们常用的UIView、UIViewController、UIApplication都继承自UIResponder,它们都能接收并处理事件)。在UIResponder中定义了上面三类事件相关的处理方法:

    事件 说明 
    触摸事件  
    - (void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event; 一根或多根手指开始触摸屏幕时执行;
    - (void)touchesMoved:(NSSet *)touches withEvent:(UIEvent *)event; 一根或多根手指在屏幕上移动时执行,注意此方法在移动过程中会重复调用;
    - (void)touchesEnded:(NSSet *)touches withEvent:(UIEvent *)event; 一根或多根手指触摸结束离开屏幕时执行;
    - (void)touchesCancelled:(NSSet *)touches withEvent:(UIEvent *)event; 触摸意外取消时执行(例如正在触摸时打入电话);
    运动事件   
    - (void)motionBegan:(UIEventSubtype)motion withEvent:(UIEvent *)event NS_AVAILABLE_IOS(3_0); 运动开始时执行;
    - (void)motionEnded:(UIEventSubtype)motion withEvent:(UIEvent *)event NS_AVAILABLE_IOS(3_0); 运动结束后执行;
    - (void)motionCancelled:(UIEventSubtype)motion withEvent:(UIEvent *)event NS_AVAILABLE_IOS(3_0); 运动被意外取消时执行;
    远程控制事件  
    - (void)remoteControlReceivedWithEvent:(UIEvent *)event NS_AVAILABLE_IOS(4_0); 接收到远程控制消息时执行;

    触摸事件

    基础知识

    三类事件中触摸事件在iOS中是最常用的事件,这里我们首先介绍触摸事件。

    在下面的例子中定义一个KCImage,它继承于UIView,在KCImage中指定一个图片作为背景。定义一个视图控制器KCTouchEventViewController,并且在其中声明一个KCImage变量,添加到视图控制器中。既然UIView和UIViewController都继承于UIResponder,那么也就就意味着所有的UIKit控件和视图控制器均能接收触摸事件。首先我们在KCTouchEventViewController中添加触摸事件,并利用触摸移动事件来移动KCImage,具体代码如下:

    //
    //  KCTouchEvenViewController.m
    //  TouchEventAndGesture
    //
    //  Created by Kenshin Cui on 14-3-16.
    //  Copyright (c) 2014年 Kenshin Cui. All rights reserved.
    //
    
    #import "KCTouchEvenViewController.h"
    #import "KCImage.h"
    
    @interface KCTouchEvenViewController (){
        KCImage *_image;
    }
    
    @end
    
    @implementation KCTouchEvenViewController
    
    - (void)viewDidLoad {
        [super viewDidLoad];
        
        _image=[[KCImage alloc]initWithFrame:CGRectMake(50, 50, 150, 169
                                                                )];
        //_image.userInteractionEnabled=NO;
        [self.view addSubview:_image];
    }
    
    #pragma mark - 视图控制器的触摸事件
    -(void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event{
        NSLog(@"UIViewController start touch...");
    }
    
    -(void)touchesMoved:(NSSet *)touches withEvent:(UIEvent *)event{
        //取得一个触摸对象(对于多点触摸可能有多个对象)
        UITouch *touch=[touches anyObject];
        //NSLog(@"%@",touch);
        
        //取得当前位置
        CGPoint current=[touch locationInView:self.view];
        //取得前一个位置
        CGPoint previous=[touch previousLocationInView:self.view];
        
        //移动前的中点位置
        CGPoint center=_image.center;
        //移动偏移量
        CGPoint offset=CGPointMake(current.x-previous.x, current.y-previous.y);
        
        //重新设置新位置
        _image.center=CGPointMake(center.x+offset.x, center.y+offset.y);
        
        NSLog(@"UIViewController moving...");
    
    }
    
    -(void)touchesEnded:(NSSet *)touches withEvent:(UIEvent *)event{
        NSLog(@"UIViewController touch end.");
    }
    @end

    现在运行程序:

    TouchEventEffect

    上面示例中我们用到了UITouch类,当执行触摸事件时会将这个对象传入。在这个对象中包含了触摸的所有信息:

    • window:触摸时所在的窗口 
    • view:触摸时所在视图 
    • tapCount:短时间内点击的次数 
    • timestamp:触摸产生或变化的时间戳 
    • phase:触摸周期内的各个状态 
    • locationInView:方法:取得在指定视图的位置 
    • previousLocationInView:方法:取得移动的前一个位置

    从上面运行效果可以看到无论是选择KCImage拖动还是在界面其他任意位置拖动都能达到移动图片的效果。既然KCImage是UIView当然在KCImage中也能触发相应的触摸事件,假设在KCImage中定义三个对应的事件:

    //
    //  KCImage.m
    //  TouchEventAndGesture
    //
    //  Created by Kenshin Cui on 14-3-16.
    //  Copyright (c) 2014年 Kenshin Cui. All rights reserved.
    //
    
    #import "KCImage.h"
    
    @implementation KCImage
    
    - (instancetype)initWithFrame:(CGRect)frame {
        self = [super initWithFrame:frame];
        if (self) {
            UIImage *img=[UIImage imageNamed:@"photo.png"];
            [self setBackgroundColor:[UIColor colorWithPatternImage:img]];
        }
        return self;
    }
    
    #pragma mark - UIView的触摸事件
    -(void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event{
        NSLog(@"UIView start touch...");
    }
    
    -(void)touchesMoved:(NSSet *)touches withEvent:(UIEvent *)event{
        NSLog(@"UIView moving...");
    }
    
    -(void)touchesEnded:(NSSet *)touches withEvent:(UIEvent *)event{
        NSLog(@"UIView touch end.");
    }
    @end

    此时如果运行程序会发现如果拖动KCImage无法达到预期的效果,但是可以发现此时会调用KCImage的触摸事件而不会调用KCTouchEventViewController中的触摸事件。如果直接拖拽其他空白位置则可以正常拖拽,而且从输出信息可以发现此时调用的是视图控制器的触摸事件。这是为什么呢?要解答这个问题我们需要了解iOS中事件的处理机制。

    事件处理机制

    在iOS中发生触摸后,事件会加入到UIApplication事件队列(在这个系列关于iOS开发的第一篇文章中我们分析iOS程序原理的时候就说过程序运行后UIApplication会循环监听用户操作),UIApplication会从事件队列取出最前面的事件并分发处理,通常先分发给应用程序主窗口,主窗口会调用hitTest:withEvent:方法(假设称为方法A,注意这是UIView的方法),查找合适的事件触发视图(这里通常称为“hit-test view”):

    1. 在顶级视图(key window的视图)上调用pointInside:withEvent:方法判断触摸点是否在当前视图内;
    2. 如果返回NO,那么A返回nil;
    3. 如果返回YES,那么它会向当前视图的所有子视图(key window的子视图)发送hitTest:withEvent:消息,遍历所有子视图的顺序是从subviews数组的末尾向前遍历(从界面最上方开始向下遍历)。
    4. 如果有subview的hitTest:withEvent:返回非空对象则A返回此对象,处理结束(注意这个过程,子视图也是根据pointInside:withEvent:的返回值来确定是返回空还是当前子视图对象的。并且这个过程中如果子视图的hidden=YES、userInteractionEnabled=NO或者alpha小于0.1都会并忽略);
    5. 如果所有subview遍历结束仍然没有返回非空对象,则A返回顶级视图;

    上面的步骤就是点击检测的过程,其实就是查找事件触发者的过程。触摸对象并非就是事件的响应者(例如上面第一个例子中没有重写KCImage触摸事件时,KCImge作为触摸对象,但是事件响应者却是UIViewController),检测到了触摸的对象之后,事件到底是如何响应呢?这个过程就必须引入一个新的概念“响应者链”。

    什么是响应者链呢?我们知道在iOS程序中无论是最后面的UIWindow还是最前面的某个按钮,它们的摆放是有前后关系的,一个控件可以放到另一个控件上面或下面,那么用户点击某个控件时是触发上面的控件还是下面的控件呢,这种先后关系构成一个链条就叫“响应者链”。在iOS中响应者链的关系可以用下图表示:

    iOS_responder_chain_2x

    当一个事件发生后首先看initial view能否处理这个事件,如果不能则会将事件传递给其上级视图(inital view的superView);如果上级视图仍然无法处理则会继续往上传递;一直传递到视图控制器view controller,首先判断视图控制器的根视图view是否能处理此事件;如果不能则接着判断该视图控制器能否处理此事件,如果还是不能则继续向上传递;(对于第二个图视图控制器本身还在另一个视图控制器中,则继续交给父视图控制器的根视图,如果根视图不能处理则交给父视图控制器处理);一直到window,如果window还是不能处理此事件则继续交给application(UIApplication单例对象)处理,如果最后application还是不能处理此事件则将其丢弃。

    这个过程大家理解起来并不难,关键问题是在这个过程中各个对象如何知道自己能不能处理该事件呢?对于继承UIResponder的对象,其不能处理事件有几个条件:

    • userInteractionEnabled=NO 
    • hidden=YES 
    • alpha=0~0.01 
    • 没有实现开始触摸方法(注意是touchesBegan:withEvent:而不是移动和结束触摸事件)

    当然前三点都是针对UIView控件或其子控件而言的,第四点可以针对UIView也可以针对视图控制器等其他UIResponder子类。对于第四种情况这里再次强调是对象中重写了开始触摸方法,则会处理这个事件,如果仅仅写了移动、停止触摸或取消触摸事件(或者这三个事件都重写了)没有写开始触摸事件,则此事件该对象不会进行处理。

    相信到了这里大家对于上面点击图片为什么不能拖拽已经很明确了。事实上通过前面的解释大家应该可以猜到即使KCImage实现了开始拖拽方法,如果在KCTouchEventViewController中设置KCImage对象的userInteractionEnabled为NO也是可以拖拽的。

    注意:上面提到hitTest:withEvent:可以指定触发事件的视图,这里就不再举例说明,这个方法重写情况比较少,一般用于自定义手势,有兴趣的童鞋可以访问:Event Delivery: The Responder Chain

     

    手势识别

    简介

    通过前面的内容我们可以看到触摸事件使用起来比较容易,但是对于多个手指触摸并进行不同的变化操作就要复杂的多了。例如说如果两个手指捏合,我们虽然在触摸开始、移动等事件中可以通过UITouchs得到两个触摸对象,但是我们如何能判断用户是用两个手指捏合还是横扫或者拖动呢?在iOS3.2之后苹果引入了手势识别,对于用户常用的手势操作进行了识别并封装成具体的类供开发者使用,这样在开发过程中我们就不必再自己编写算法识别用户的触摸操作了。在iOS中有六种手势操作:

    手势 说明
    UITapGestureRecognizer 点按手势
    UIPinchGestureRecognizer 捏合手势
    UIPanGestureRecognizer 拖动手势
    UISwipeGestureRecognizer 轻扫手势,支持四个方向的轻扫,但是不同的方向要分别定义轻扫手势
    UIRotationGestureRecognizer 旋转手势
    UILongPressGestureRecognizer 长按手势

    所有的手势操作都继承于UIGestureRecognizer,这个类本身不能直接使用。这个类中定义了这几种手势共有的一些属性和方法(下表仅列出常用属性和方法):

    名称 说明
    属性  
    @property(nonatomic,readonly) UIGestureRecognizerState state; 手势状态
    @property(nonatomic, getter=isEnabled) BOOL enabled; 手势是否可用
    @property(nonatomic,readonly) UIView *view; 触发手势的视图(一般在触摸执行操作中我们可以通过此属性获得触摸视图进行操作)
    @property(nonatomic) BOOL delaysTouchesBegan; 手势识别失败前不执行触摸开始事件,默认为NO;如果为YES,那么成功识别则不执行触摸开始事件,失败则执行触摸开始事件;如果为NO,则不管成功与否都执行触摸开始事件;
    方法  
    - (void)addTarget:(id)target action:(SEL)action; 添加触摸执行事件
    - (void)removeTarget:(id)target action:(SEL)action; 移除触摸执行事件
    - (NSUInteger)numberOfTouches; 触摸点的个数(同时触摸的手指数)
    - (CGPoint)locationInView:(UIView*)view;  在指定视图中的相对位置
    - (CGPoint)locationOfTouch:(NSUInteger)touchIndex inView:(UIView*)view; 触摸点相对于指定视图的位置
    - (void)requireGestureRecognizerToFail:(UIGestureRecognizer *)otherGestureRecognizer; 指定一个手势需要另一个手势执行失败才会执行
    代理方法  
    - (BOOL)gestureRecognizer:(UIGestureRecognizer *)gestureRecognizer shouldRecognizeSimultaneouslyWithGestureRecognizer:(UIGestureRecognizer *)otherGestureRecognizer; 一个控件的手势识别后是否阻断手势识别继续向下传播,默认返回NO;如果为YES,响应者链上层对象触发手势识别后,如果下层对象也添加了手势并成功识别也会继续执行,否则上层对象识别后则不再继续传播;

    手势状态

    这里着重解释一下上表中手势状态这个对象。在六种手势识别中,只有一种手势是离散手势,它就是UITapGestureRecgnier。离散手势的特点就是一旦识别就无法取消,而且只会调用一次手势操作事件(初始化手势时指定的触发方法)。换句话说其他五种手势是连续手势,连续手势的特点就是会多次调用手势操作事件,而且在连续手势识别后可以取消手势。从下图可以看出两者调用操作事件的次数是不同的:

    discrete_vs_continuous_2x

    在iOS中将手势状态分为如下几种:

    typedef NS_ENUM(NSInteger, UIGestureRecognizerState) {
        UIGestureRecognizerStatePossible,   // 尚未识别是何种手势操作(但可能已经触发了触摸事件),默认状态
        
        UIGestureRecognizerStateBegan,      // 手势已经开始,此时已经被识别,但是这个过程中可能发生变化,手势操作尚未完成
        UIGestureRecognizerStateChanged,    // 手势状态发生转变
        UIGestureRecognizerStateEnded,      // 手势识别操作完成(此时已经松开手指)
        UIGestureRecognizerStateCancelled,  // 手势被取消,恢复到默认状态
        
        UIGestureRecognizerStateFailed,     // 手势识别失败,恢复到默认状态
        
        UIGestureRecognizerStateRecognized = UIGestureRecognizerStateEnded // 手势识别完成,同UIGestureRecognizerStateEnded
    };
    • 对于离散型手势UITapGestureRecgnizer要么被识别,要么失败,点按(假设点按次数设置为1,并且没有添加长按手势)下去一次不松开则此时什么也不会发生,松开手指立即识别并调用操作事件,并且状态为3(已完成)。 
    • 但是连续手势要复杂一些,就拿旋转手势来说,如果两个手指点下去不做任何操作,此时并不能识别手势(因为我们还没旋转)但是其实已经触发了触摸开始事件,此时处于状态0;如果此时旋转会被识别,也就会调用对应的操作事件,同时状态变成1(手势开始),但是状态1只有一瞬间;紧接着状态变为2(因为我们的旋转需要持续一会),并且重复调用操作事件(如果在事件中打印状态会重复打印2);松开手指,此时状态变为3,并调用1次操作事件。

    为了大家更好的理解这个状态的变化,不妨在操作事件中打印事件状态,会发现在操作事件中的状态永远不可能为0(默认状态),因为只要调用此事件说明已经被识别了。前面也说过,手势识别从根本还是调用触摸事件而完成的,连续手势之所以会发生状态转换完全是由于触摸事件中的移动事件造成的,没有移动事件也就不存在这个过程中状态变化。

    大家通过苹果官方的分析图再理解一下上面说的内容:

    gr_state_transitions_2x

    使用手势

    在iOS中添加手势比较简单,可以归纳为以下几个步骤:

    1. 创建对应的手势对象; 
    2. 设置手势识别属性【可选】; 
    3. 附加手势到指定的对象; 
    4. 编写手势操作方法;

    为了帮助大家理解,下面以一个图片查看程序演示一下上面几种手势,在这个程序中我们完成以下功能:

    如果点按图片会在导航栏显示图片名称;

    如果长按图片会显示删除按钮,提示用户是否删除;

    如果捏合会放大、缩小图片;

    如果轻扫会切换到下一张或上一张图片;

    如果旋转会旋转图片;

    如果拖动会移动图片;

    具体布局草图如下:

    PhotoViewerPrototype

    为了显示导航条,我们首先将主视图控制器KCPhotoViewController放入一个导航控制器,然后在主视图控制器中放一个UIImage用于展示图片。下面是主要代码:

    //
    //  KCGestureViewController.m
    //  TouchEventAndGesture
    //
    //  Created by Kenshin Cui on 14-3-16.
    //  Copyright (c) 2014年 Kenshin Cui. All rights reserved.
    //
    
    #import "KCPhotoViewController.h"
    #define kImageCount 3
    
    @interface KCPhotoViewController (){
        UIImageView *_imageView;//图片展示控件
        int _currentIndex;//当前图片索引
    }
    
    @end
    
    @implementation KCPhotoViewController
    
    - (void)viewDidLoad {
        [super viewDidLoad];
        
        [self initLayout];
    
        [self addGesture];
    }
    
    
    #pragma mark 布局
    -(void)initLayout{
        /*添加图片展示控件*/
        CGSize screenSize=[UIScreen mainScreen].applicationFrame.size;
        CGFloat topPadding=20;
        CGFloat y=22+44+topPadding,height=screenSize.height-y-topPadding;
        
        CGRect imageFrame=CGRectMake(0, y, screenSize.width, height);
        _imageView=[[UIImageView alloc]initWithFrame:imageFrame];
        _imageView.contentMode=UIViewContentModeScaleToFill;//设置内容模式为缩放填充
        _imageView.userInteractionEnabled=YES;//这里必须设置为YES,否则无法接收手势操作
        [self.view addSubview:_imageView];
        
        //添加默认图片
        UIImage *image=[UIImage imageNamed:@"0.jpg"];
        _imageView.image=image;
        [self showPhotoName];
        
    }
    
    #pragma mark 添加手势
    -(void)addGesture{
        /*添加点按手势*/
        //创建手势对象
        UITapGestureRecognizer *tapGesture=[[UITapGestureRecognizer alloc]initWithTarget:self action:@selector(tapImage:)];
        //设置手势属性
        tapGesture.numberOfTapsRequired=1;//设置点按次数,默认为1,注意在iOS中很少用双击操作
        tapGesture.numberOfTouchesRequired=1;//点按的手指数
        //添加手势到对象(注意,这里添加到了控制器视图中,而不是图片上,否则点击空白无法隐藏导航栏)
        [self.view addGestureRecognizer:tapGesture];
        
        
        /*添加长按手势*/
        UILongPressGestureRecognizer *longPressGesture=[[UILongPressGestureRecognizer alloc]initWithTarget:self action:@selector(longPressImage:)];
        longPressGesture.minimumPressDuration=0.5;//设置长按时间,默认0.5秒,一般这个值不要修改
        //注意由于我们要做长按提示删除操作,因此这个手势不再添加到控制器视图上而是添加到了图片上
        [_imageView addGestureRecognizer:longPressGesture];
        
        /*添加捏合手势*/
        UIPinchGestureRecognizer *pinchGesture=[[UIPinchGestureRecognizer alloc]initWithTarget:self action:@selector(pinchImage:)];
        [self.view addGestureRecognizer:pinchGesture];
        
        /*添加旋转手势*/
        UIRotationGestureRecognizer *rotationGesture=[[UIRotationGestureRecognizer alloc]initWithTarget:self action:@selector(rotateImage:)];
        [self.view addGestureRecognizer:rotationGesture];
        
        /*添加拖动手势*/
        UIPanGestureRecognizer *panGesture=[[UIPanGestureRecognizer alloc]initWithTarget:self action:@selector(panImage:)];
        [_imageView addGestureRecognizer:panGesture];
        
        /*添加轻扫手势*/
        //注意一个轻扫手势只能控制一个方向,默认向右,通过direction进行方向控制
        UISwipeGestureRecognizer *swipeGestureToRight=[[UISwipeGestureRecognizer alloc]initWithTarget:self action:@selector(swipeImage:)];
        //swipeGestureToRight.direction=UISwipeGestureRecognizerDirectionRight;//默认为向右轻扫
        [self.view addGestureRecognizer:swipeGestureToRight];
        
        UISwipeGestureRecognizer *swipeGestureToLeft=[[UISwipeGestureRecognizer alloc]initWithTarget:self action:@selector(swipeImage:)];
        swipeGestureToLeft.direction=UISwipeGestureRecognizerDirectionLeft;
        [self.view addGestureRecognizer:swipeGestureToLeft];
    
    }
    
    #pragma mark 显示图片名称
    -(void)showPhotoName{
        NSString *title=[NSString stringWithFormat:@"%i.jpg",_currentIndex];
        [self setTitle:title];
    }
    
    #pragma mark 下一张图片
    -(void)nextImage{
        int index=(_currentIndex+kImageCount+1)%kImageCount;
        NSString *imageName=[NSString stringWithFormat:@"%i.jpg",index];
        _imageView.image=[UIImage imageNamed:imageName];
        _currentIndex=index;
        [self showPhotoName];
    }
    
    #pragma mark 上一张图片
    -(void)lastImage{
        int index=(_currentIndex+kImageCount-1)%kImageCount;
        NSString *imageName=[NSString stringWithFormat:@"%i.jpg",index];
        _imageView.image=[UIImage imageNamed:imageName];
        _currentIndex=index;
        [self showPhotoName];
    }
    
    #pragma mark - 手势操作
    #pragma mark 点按隐藏或显示导航栏
    -(void)tapImage:(UITapGestureRecognizer *)gesture{
        //NSLog(@"tap:%i",gesture.state);
        BOOL hidden=!self.navigationController.navigationBarHidden;
        [self.navigationController setNavigationBarHidden:hidden animated:YES];
    }
    
    #pragma mark 长按提示是否删除
    -(void)longPressImage:(UILongPressGestureRecognizer *)gesture{
        //NSLog(@"longpress:%i",gesture.state);
        //注意其实在手势里面有一个view属性可以获取点按的视图
        //UIImageView *imageView=(UIImageView *)gesture.view;
        
        //由于连续手势此方法会调用多次,所以需要判断其手势状态
        if (gesture.state==UIGestureRecognizerStateBegan) {
            UIActionSheet *actionSheet=[[UIActionSheet alloc]initWithTitle:@"System Info" delegate:nil cancelButtonTitle:@"Cancel" destructiveButtonTitle:@"Delete the photo" otherButtonTitles:nil];
            [actionSheet showInView:self.view];
    
        }
    }
    
    #pragma mark 捏合时缩放图片
    -(void)pinchImage:(UIPinchGestureRecognizer *)gesture{
        //NSLog(@"pinch:%i",gesture.state);
        
        if (gesture.state==UIGestureRecognizerStateChanged) {
            //捏合手势中scale属性记录的缩放比例
            _imageView.transform=CGAffineTransformMakeScale(gesture.scale, gesture.scale);
        }else if(gesture.state==UIGestureRecognizerStateEnded){//结束后恢复
            [UIView animateWithDuration:.5 animations:^{
                _imageView.transform=CGAffineTransformIdentity;//取消一切形变
            }];
        }
    }
    
    #pragma mark 旋转图片
    -(void)rotateImage:(UIRotationGestureRecognizer *)gesture{
        //NSLog(@"rotate:%i",gesture.state);
        if (gesture.state==UIGestureRecognizerStateChanged) {
            //旋转手势中rotation属性记录了旋转弧度
            _imageView.transform=CGAffineTransformMakeRotation(gesture.rotation);
        }else if(gesture.state==UIGestureRecognizerStateEnded){
            [UIView animateWithDuration:0.8 animations:^{
                _imageView.transform=CGAffineTransformIdentity;//取消形变
            }];
        }
    }
    
    #pragma mark 拖动图片
    -(void)panImage:(UIPanGestureRecognizer *)gesture{
        if (gesture.state==UIGestureRecognizerStateChanged) {
            CGPoint translation=[gesture translationInView:self.view];//利用拖动手势的translationInView:方法取得在相对指定视图(这里是控制器根视图)的移动
            _imageView.transform=CGAffineTransformMakeTranslation(translation.x, translation.y);
        }else if(gesture.state==UIGestureRecognizerStateEnded){
            [UIView animateWithDuration:0.5 animations:^{
                _imageView.transform=CGAffineTransformIdentity;
            }];
        }
        
    }
    
    #pragma mark 轻扫则查看下一张或上一张
    //注意虽然轻扫手势是连续手势,但是只有在识别结束才会触发,不用判断状态
    -(void)swipeImage:(UISwipeGestureRecognizer *)gesture{
    //    NSLog(@"swip:%i",gesture.state);
    //    if (gesture.state==UIGestureRecognizerStateEnded) {
        
            //direction记录的轻扫的方向
            if (gesture.direction==UISwipeGestureRecognizerDirectionRight) {//向右
                [self nextImage];
    //            NSLog(@"right");
            }else if(gesture.direction==UISwipeGestureRecognizerDirectionLeft){//向左
    //            NSLog(@"left");
                [self lastImage];
            }
    //    }
    }
    
    
    //-(void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event{
    //    //NSLog(@"touch begin...");
    //}
    @end

    运行效果:

    GestureRecognizerEffect

    在上面示例中需要强调几点:

    • UIImageView默认是不支持交互的,也就是userInteractionEnabled=NO ,因此要接收触摸事件(手势识别),必须设置userInteractionEnabled=YES(在iOS中UILabel、UIImageView的userInteractionEnabled默认都是NO,UIButton、UITextField、UIScrollView、UITableView等默认都是YES)。 
    • 轻扫手势虽然是连续手势但是它的操作事件只会在识别结束时调用一次,其他连续手势都会调用多次,一般需要进行状态判断;此外轻扫手势支持四个方向,但是如果要支持多个方向需要添加多个轻扫手势。

    手势冲突

    细心的童鞋会发现在上面的演示效果图中当切换到下一张或者上一张图片时并没有轻扫图片而是在空白地方轻扫完成,原因是如果我轻扫图片会引起拖动手势而不是轻扫手势。换句话说,两种手势发生了冲突。

    冲突的原因很简单,拖动手势的操作事件是在手势的开始状态(状态1)识别执行的,而轻扫手势的操作事件只有在手势结束状态(状态3)才能执行,因此轻扫手势就作为了牺牲品没有被正确识别。我们理想的情况当然是如果在图片上拖动就移动图片,如果在图片上轻扫就翻动图片。如何解决这个冲突呢?

    在iOS中,如果一个手势A的识别部分是另一个手势B的子部分时,默认情况下A就会先识别,B就无法识别了。要解决这个冲突可以利用- (void)requireGestureRecognizerToFail:(UIGestureRecognizer *)otherGestureRecognizer;方法来完成。正是前面表格中UIGestureRecognizer的最后一个方法,这个方法可以指定某个手势执行的前提是另一个手势失败才会识别执行。也就是说如果我们指定拖动手势的执行前提为轻扫手势失败就可以了,这样一来当我们手指轻轻滑动时系统会优先考虑轻扫手势,如果最后发现该操作不是轻扫,那么就会执行拖动。只要将下面的代码添加到添加手势之后就能解决这个问题了(注意为了更加清晰的区分拖动和轻扫[模拟器中拖动稍微快一点就识别成了轻扫],这里将长按手势的前提设置为拖动失败,避免演示拖动时长按手势会被识别):

        
        //解决在图片上滑动时拖动手势和轻扫手势的冲突
        [panGesture requireGestureRecognizerToFail:swipeGestureToRight];
        [panGesture requireGestureRecognizerToFail:swipeGestureToLeft];
        //解决拖动和长按手势之间的冲突
        [longPressGesture requireGestureRecognizerToFail:panGesture];

    运行效果:

    GestureRecognizerEffect2

    两个不同控件的手势同时执行

    我们知道在iOS的触摸事件中,事件触发是根据响应者链进行的,上层触摸事件执行后就不再向下传播。默认情况下手势也是类似的,先识别的手势会阻断手势识别操作继续传播。那么如何让两个有层次关系并且都添加了手势的控件都能正确识别手势呢?答案就是利用代理的-(BOOL)gestureRecognizer:(UIGestureRecognizer *)gestureRecognizer shouldRecognizeSimultaneouslyWithGestureRecognizer:(UIGestureRecognizer *)otherGestureRecognizer方法。这个代理方法默认返回NO,会阻断继续向下识别手势,如果返回YES则可以继续向下传播识别。

    下面的代码控制演示了当在图片上长按时同时可以识别控制器视图的长按手势(注意其中我们还控制了只有在UIImageView中操作的手势才能向下传递,如果不控制则所有控件都可以向下传递)

    //
    //  KCGestureViewController.m
    //  TouchEventAndGesture
    //
    //  Created by Kenshin Cui on 14-3-16.
    //  Copyright (c) 2014年 Kenshin Cui. All rights reserved.
    //
    
    #import "KCPhotoViewController.h"
    #define kImageCount 3
    
    @interface KCPhotoViewController ()<UIGestureRecognizerDelegate>{
        UIImageView *_imageView;//图片展示控件
        int _currentIndex;//当前图片索引
    }
    
    @end
    
    @implementation KCPhotoViewController
    
    - (void)viewDidLoad {
        [super viewDidLoad];
        
        [self initLayout];
    
        [self addGesture];
    }
    
    
    #pragma mark 布局
    -(void)initLayout{
        /*添加图片展示控件*/
        CGSize screenSize=[UIScreen mainScreen].applicationFrame.size;
        CGFloat topPadding=20;
        CGFloat y=22+44+topPadding,height=screenSize.height-y-topPadding;
        
        CGRect imageFrame=CGRectMake(0, y, screenSize.width, height);
        _imageView=[[UIImageView alloc]initWithFrame:imageFrame];
        _imageView.contentMode=UIViewContentModeScaleToFill;//设置内容模式为缩放填充
        _imageView.userInteractionEnabled=YES;//这里必须设置位YES,否则无法接收手势操作
        //_imageView.multipleTouchEnabled=YES;//支持多点触摸,默认就是YES
        [self.view addSubview:_imageView];
        
        //添加默认图片
        UIImage *image=[UIImage imageNamed:@"0.jpg"];
        _imageView.image=image;
        [self showPhotoName];
        
    }
    
    #pragma mark 添加手势
    -(void)addGesture{
        /*添加点按手势*/
        //创建手势对象
        UITapGestureRecognizer *tapGesture=[[UITapGestureRecognizer alloc]initWithTarget:self action:@selector(tapImage:)];
        //设置手势属性
        tapGesture.numberOfTapsRequired=1;//设置点按次数,默认为1,注意在iOS中很少用双击操作
        tapGesture.numberOfTouchesRequired=1;//点按的手指数
        //添加手势到对象(注意,这里添加到了控制器视图中,而不是图片上,否则点击空白无法隐藏导航栏)
        [self.view addGestureRecognizer:tapGesture];
        
        
        /*添加长按手势*/
        UILongPressGestureRecognizer *longPressGesture=[[UILongPressGestureRecognizer alloc]initWithTarget:self action:@selector(longPressImage:)];
        longPressGesture.minimumPressDuration=0.5;//设置长按时间,默认0.5秒,一般这个值不要修改
        //注意由于我们要做长按提示删除操作,因此这个手势不再添加到控制器视图上而是添加到了图片上
        [_imageView addGestureRecognizer:longPressGesture];
        
        /*添加捏合手势*/
        UIPinchGestureRecognizer *pinchGesture=[[UIPinchGestureRecognizer alloc]initWithTarget:self action:@selector(pinchImage:)];
        [self.view addGestureRecognizer:pinchGesture];
        
        /*添加旋转手势*/
        UIRotationGestureRecognizer *rotationGesture=[[UIRotationGestureRecognizer alloc]initWithTarget:self action:@selector(rotateImage:)];
        [self.view addGestureRecognizer:rotationGesture];
        
        /*添加拖动手势*/
        UIPanGestureRecognizer *panGesture=[[UIPanGestureRecognizer alloc]initWithTarget:self action:@selector(panImage:)];
        [_imageView addGestureRecognizer:panGesture];
        
        /*添加轻扫手势*/
        //注意一个轻扫手势只能控制一个方向,默认向右,通过direction进行方向控制
        UISwipeGestureRecognizer *swipeGestureToRight=[[UISwipeGestureRecognizer alloc]initWithTarget:self action:@selector(swipeImage:)];
        //swipeGestureToRight.direction=UISwipeGestureRecognizerDirectionRight;//默认位向右轻扫
        [self.view addGestureRecognizer:swipeGestureToRight];
        
        UISwipeGestureRecognizer *swipeGestureToLeft=[[UISwipeGestureRecognizer alloc]initWithTarget:self action:@selector(swipeImage:)];
        swipeGestureToLeft.direction=UISwipeGestureRecognizerDirectionLeft;
        [self.view addGestureRecognizer:swipeGestureToLeft];
        
        //解决在图片上滑动时拖动手势和轻扫手势的冲突
        [panGesture requireGestureRecognizerToFail:swipeGestureToRight];
        [panGesture requireGestureRecognizerToFail:swipeGestureToLeft];
        //解决拖动和长按手势之间的冲突
        [longPressGesture requireGestureRecognizerToFail:panGesture];
        
        
        /*演示不同视图的手势同时执行
         *在上面_imageView已经添加了长按手势,这里给视图控制器的视图也加上长按手势让两者都执行
         *
         */
        self.view.tag=100;
        _imageView.tag=200;
        UILongPressGestureRecognizer *viewLongPressGesture=[[UILongPressGestureRecognizer alloc]initWithTarget:self action:@selector(longPressView:)];
        viewLongPressGesture.delegate=self;
        [self.view addGestureRecognizer:viewLongPressGesture];
    
    }
    
    #pragma mark 显示图片名称
    -(void)showPhotoName{
        NSString *title=[NSString stringWithFormat:@"%i.jpg",_currentIndex];
        [self setTitle:title];
    }
    
    #pragma mark 下一张图片
    -(void)nextImage{
        int index=(_currentIndex+kImageCount+1)%kImageCount;
        NSString *imageName=[NSString stringWithFormat:@"%i.jpg",index];
        _imageView.image=[UIImage imageNamed:imageName];
        _currentIndex=index;
        [self showPhotoName];
    }
    
    #pragma mark 上一张图片
    -(void)lastImage{
        int index=(_currentIndex+kImageCount-1)%kImageCount;
        NSString *imageName=[NSString stringWithFormat:@"%i.jpg",index];
        _imageView.image=[UIImage imageNamed:imageName];
        _currentIndex=index;
        [self showPhotoName];
    }
    
    #pragma mark - 手势操作
    #pragma mark 点按隐藏或显示导航栏
    -(void)tapImage:(UITapGestureRecognizer *)gesture{
        //NSLog(@"tap:%i",gesture.state);
        BOOL hidden=!self.navigationController.navigationBarHidden;
        [self.navigationController setNavigationBarHidden:hidden animated:YES];
    }
    
    #pragma mark 长按提示是否删除
    -(void)longPressImage:(UILongPressGestureRecognizer *)gesture{
        //NSLog(@"longpress:%i",gesture.state);
        //注意其实在手势里面有一个view属性可以获取点按的视图
        //UIImageView *imageView=(UIImageView *)gesture.view;
        
        //由于连续手势此方法会调用多次,所以需求判断其手势状态
        if (gesture.state==UIGestureRecognizerStateBegan) {
            UIActionSheet *actionSheet=[[UIActionSheet alloc]initWithTitle:@"System Info" delegate:nil cancelButtonTitle:@"Cancel" destructiveButtonTitle:@"Delete the photo" otherButtonTitles:nil];
            [actionSheet showInView:self.view];
    
        }
    }
    
    #pragma mark 捏合时缩放图片
    -(void)pinchImage:(UIPinchGestureRecognizer *)gesture{
        //NSLog(@"pinch:%i",gesture.state);
        
        if (gesture.state==UIGestureRecognizerStateChanged) {
            //捏合手势中scale属性记录的缩放比例
            _imageView.transform=CGAffineTransformMakeScale(gesture.scale, gesture.scale);
        }else if(gesture.state==UIGestureRecognizerStateEnded){//结束后恢复
            [UIView animateWithDuration:.5 animations:^{
                _imageView.transform=CGAffineTransformIdentity;//取消一切形变
            }];
        }
    }
    
    #pragma mark 旋转图片
    -(void)rotateImage:(UIRotationGestureRecognizer *)gesture{
        //NSLog(@"rotate:%i",gesture.state);
        if (gesture.state==UIGestureRecognizerStateChanged) {
            //旋转手势中rotation属性记录了旋转弧度
            _imageView.transform=CGAffineTransformMakeRotation(gesture.rotation);
        }else if(gesture.state==UIGestureRecognizerStateEnded){
            [UIView animateWithDuration:0.8 animations:^{
                _imageView.transform=CGAffineTransformIdentity;//取消形变
            }];
        }
    }
    
    #pragma mark 拖动图片
    -(void)panImage:(UIPanGestureRecognizer *)gesture{
        if (gesture.state==UIGestureRecognizerStateChanged) {
            CGPoint translation=[gesture translationInView:self.view];//利用拖动手势的translationInView:方法取得在相对指定视图(控制器根视图)的移动
            _imageView.transform=CGAffineTransformMakeTranslation(translation.x, translation.y);
        }else if(gesture.state==UIGestureRecognizerStateEnded){
            [UIView animateWithDuration:0.5 animations:^{
                _imageView.transform=CGAffineTransformIdentity;
            }];
        }
        
    }
    
    #pragma mark 轻扫则查看下一张或上一张
    //注意虽然轻扫手势是连续手势,但是只有在识别结束才会触发,不用判断状态
    -(void)swipeImage:(UISwipeGestureRecognizer *)gesture{
    //    NSLog(@"swip:%i",gesture.state);
    //    if (gesture.state==UIGestureRecognizerStateEnded) {
        
            //direction记录的轻扫的方向
            if (gesture.direction==UISwipeGestureRecognizerDirectionRight) {//向右
                [self nextImage];
    //            NSLog(@"right");
            }else if(gesture.direction==UISwipeGestureRecognizerDirectionLeft){//向左
    //            NSLog(@"left");
                [self lastImage];
            }
    //    }
    }
    
    
    
    #pragma mark 控制器视图的长按手势
    -(void)longPressView:(UILongPressGestureRecognizer *)gesture{
        NSLog(@"view long press!");
    }
    
    
    #pragma mark 手势代理方法
    -(BOOL)gestureRecognizer:(UIGestureRecognizer *)gestureRecognizer shouldRecognizeSimultaneouslyWithGestureRecognizer:(UIGestureRecognizer *)otherGestureRecognizer{
        //NSLog(@"%i,%i",gestureRecognizer.view.tag,otherGestureRecognizer.view.tag);
        
        //注意,这里控制只有在UIImageView中才能向下传播,其他情况不允许
        if ([otherGestureRecognizer.view isKindOfClass:[UIImageView class]]) {
            return YES;
        }
        return NO;
    }
    
    #pragma mark - 触摸事件
    -(void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event{
        NSLog(@"touch begin...");
    }
    -(void)touchesEnded:(NSSet *)touches withEvent:(UIEvent *)event{
        NSLog(@"touch end.");
    }
    @end

    运动事件

    前面我们主要介绍了触摸事件以及由触摸事件引出的手势识别,下面我们简单介绍一下运动事件。在iOS中和运动相关的有三个事件:开始运动、结束运动、取消运动。

    监听运动事件对于UI控件有个前提就是监听对象必须是第一响应者(对于UIViewController视图控制器和UIAPPlication没有此要求)。这也就意味着如果监听的是一个UI控件那么-(BOOL)canBecomeFirstResponder;方法必须返回YES。同时控件显示时(在-(void)viewWillAppear:(BOOL)animated;事件中)调用视图控制器的becomeFirstResponder方法。当视图不再显示时(在-(void)viewDidDisappear:(BOOL)animated;事件中)注销第一响应者身份。

    由于视图控制器默认就可以调用运动开始、运动结束事件在此不再举例。现在不妨假设我们现在在开发一个摇一摇找人的功能,这里我们就自定义一个图片展示控件,在这个图片控件中我们可以通过摇晃随机切换界面图片。代码比较简单:

    KCImageView.m

    //
    //  KCImageView.m
    //  TouchEventAndGesture
    //
    //  Created by Kenshin Cui on 14-3-16.
    //  Copyright (c) 2014年 Kenshin Cui. All rights reserved.
    //
    
    #import "KCImageView.h"
    #define kImageCount 3
    
    @implementation KCImageView
    
    - (instancetype)initWithFrame:(CGRect)frame {
        self = [super initWithFrame:frame];
        if (self) {
            self.image=[self getImage];
        }
        return self;
    }
    
    #pragma mark 设置控件可以成为第一响应者
    -(BOOL)canBecomeFirstResponder{
        return YES;
    }
    
    #pragma mark 运动开始
    -(void)motionBegan:(UIEventSubtype)motion withEvent:(UIEvent *)event{
        //这里只处理摇晃事件
        if (motion==UIEventSubtypeMotionShake) {
            self.image=[self getImage];
        }
    }
    #pragma mark 运动结束
    -(void)motionEnded:(UIEventSubtype)motion withEvent:(UIEvent *)event{
        
    }
    
    
    #pragma mark 随机取得图片
    -(UIImage *)getImage{
        int index= arc4random()%kImageCount;
        NSString *imageName=[NSString stringWithFormat:@"avatar%i.png",index];
        UIImage *image=[UIImage imageNamed:imageName];
        return image;
    }
    @end

    KCShakeViewController.m

    //
    //  KCShakeViewController.m
    //  TouchEventAndGesture
    //
    //  Created by Kenshin Cui on 14-3-16.
    //  Copyright (c) 2014年 Kenshin Cui. All rights reserved.
    //
    
    #import "KCShakeViewController.h"
    #import "KCImageView.h"
    
    @interface KCShakeViewController (){
        KCImageView *_imageView;
    }
    
    @end
    
    @implementation KCShakeViewController
    
    - (void)viewDidLoad {
        [super viewDidLoad];
        
        
    }
    
    #pragma mark 视图显示时让控件变成第一响应者
    -(void)viewDidAppear:(BOOL)animated{
        _imageView=[[KCImageView alloc]initWithFrame:[UIScreen mainScreen].applicationFrame];
        _imageView.userInteractionEnabled=true;
        [self.view addSubview:_imageView];
        [_imageView becomeFirstResponder];
    }
    
    #pragma mark 视图不显示时注销控件第一响应者的身份
    -(void)viewDidDisappear:(BOOL)animated{
        [_imageView resignFirstResponder];
    }
    
    /*视图控制器的运动事件*/
    //-(void)motionBegan:(UIEventSubtype)motion withEvent:(UIEvent *)event{
    //    NSLog(@"motion begin...");
    //}
    //
    //-(void)motionEnded:(UIEventSubtype)motion withEvent:(UIEvent *)event{
    //    NSLog(@"motion end.");
    //}
    
    @end

    运行效果(下图演示时使用了模拟器摇晃操作的快捷键,没有使用鼠标操作):

    MotionEffect

    远程控制事件

    在今天的文章中还剩下最后一类事件:远程控制,远程控制事件这里主要说的就是耳机线控操作。在前面的事件列表中,大家可以看到在iOS中和远程控制事件有关的只有一个- (void)remoteControlReceivedWithEvent:(UIEvent *)event NS_AVAILABLE_IOS(4_0);事件要监听到这个事件有三个前提(视图控制器UIViewController或应用程序UIApplication只有两个)

    • 启用远程事件接收(使用[[UIApplication sharedApplication] beginReceivingRemoteControlEvents];方法)。 
    • 对于UI控件同样要求必须是第一响应者(对于视图控制器UIViewController或者应用程序UIApplication对象监听无此要求)。 
    • 应用程序必须是当前音频的控制者,也就是在iOS 7中通知栏中当前音频播放程序必须是我们自己开发程序。

    基于第三点我们必须明确,如果我们的程序不想要控制音频,只是想利用远程控制事件做其他的事情,例如模仿iOS7中的按音量+键拍照是做不到的,目前iOS7给我们的远程控制权限还仅限于音频控制(当然假设我们确实想要做一个和播放音频无关的应用但是又想进行远程控制,也可以隐藏一个音频播放操作,拿到远程控制操作权后进行远程控制)。

    运动事件中我们也提到一个枚举类型UIEventSubtype,而且我们利用它来判断是否运动事件,在枚举中还包含了我们运程控制的子事件类型,我们先来熟悉一下这个枚举(从远程控制子事件类型也不难发现它和音频播放有密切关系):

    typedef NS_ENUM(NSInteger, UIEventSubtype) {
        // 不包含任何子事件类型
        UIEventSubtypeNone                              = 0,
        
        // 摇晃事件(从iOS3.0开始支持此事件)
        UIEventSubtypeMotionShake                       = 1,
        
        //远程控制子事件类型(从iOS4.0开始支持远程控制事件)
        //播放事件【操作:停止状态下,按耳机线控中间按钮一下】
        UIEventSubtypeRemoteControlPlay                 = 100,
        //暂停事件
        UIEventSubtypeRemoteControlPause                = 101,
        //停止事件
        UIEventSubtypeRemoteControlStop                 = 102,
        //播放或暂停切换【操作:播放或暂停状态下,按耳机线控中间按钮一下】
        UIEventSubtypeRemoteControlTogglePlayPause      = 103,
        //下一曲【操作:按耳机线控中间按钮两下】
        UIEventSubtypeRemoteControlNextTrack            = 104,
        //上一曲【操作:按耳机线控中间按钮三下】
        UIEventSubtypeRemoteControlPreviousTrack        = 105,
        //快退开始【操作:按耳机线控中间按钮三下不要松开】
        UIEventSubtypeRemoteControlBeginSeekingBackward = 106,
        //快退停止【操作:按耳机线控中间按钮三下到了快退的位置松开】
        UIEventSubtypeRemoteControlEndSeekingBackward   = 107,
        //快进开始【操作:按耳机线控中间按钮两下不要松开】
        UIEventSubtypeRemoteControlBeginSeekingForward  = 108,
        //快进停止【操作:按耳机线控中间按钮两下到了快进的位置松开】
        UIEventSubtypeRemoteControlEndSeekingForward    = 109,
    };

    这里我们将远程控制事件放到视图控制器(事实上很少直接添加到UI控件,一般就是添加到UIApplication或者UIViewController),模拟一个音乐播放器。

    1.首先在应用程序启动后设置接收远程控制事件,并且设置音频会话保证后台运行可以播放(注意要在应用配置中设置允许多任务)

    //
    //  AppDelegate.m
    //  TouchEventAndGesture
    //
    //  Created by Kenshin Cui on 14-3-16.
    //  Copyright (c) 2014年 Kenshin Cui. All rights reserved.
    //
    
    #import "AppDelegate.h"
    #import "ViewController.h"
    #import <AVFoundation/AVFoundation.h>
    #import "KCApplication.h"
    
    @interface AppDelegate ()
    
    @end
    
    @implementation AppDelegate
                
    
    - (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions {
        _window=[[UIWindow alloc]initWithFrame:[UIScreen mainScreen].bounds];
        
        _window.backgroundColor =[UIColor colorWithRed:249/255.0 green:249/255.0 blue:249/255.0 alpha:1];
        
        //设置全局导航条风格和颜色
        [[UINavigationBar appearance] setBarTintColor:[UIColor colorWithRed:23/255.0 green:180/255.0 blue:237/255.0 alpha:1]];
        [[UINavigationBar appearance] setBarStyle:UIBarStyleBlack];
    
        ViewController *mainController=[[ViewController alloc]init];
        _window.rootViewController=mainController;
        
        //设置播放会话,在后台可以继续播放(还需要设置程序允许后台运行模式)
        [[AVAudioSession sharedInstance] setCategory:AVAudioSessionCategoryPlayback error:nil];
        if(![[AVAudioSession sharedInstance] setActive:YES error:nil])
        {
            NSLog(@"Failed to set up a session.");
        }
        
        
        //启用远程控制事件接收
        [[UIApplication sharedApplication] beginReceivingRemoteControlEvents];
    //    [self becomeFirstResponder];
         
        [_window makeKeyAndVisible];
    
    
        return YES;
    }
    
    //-(void)remoteControlReceivedWithEvent:(UIEvent *)event{
    //    NSLog(@"remote");
    //}
    
    - (void)applicationWillResignActive:(UIApplication *)application {
        // Sent when the application is about to move from active to inactive state. This can occur for certain types of temporary interruptions (such as an incoming phone call or SMS message) or when the user quits the application and it begins the transition to the background state.
        // Use this method to pause ongoing tasks, disable timers, and throttle down OpenGL ES frame rates. Games should use this method to pause the game.
    }
    
    - (void)applicationDidEnterBackground:(UIApplication *)application {
        // Use this method to release shared resources, save user data, invalidate timers, and store enough application state information to restore your application to its current state in case it is terminated later.
        // If your application supports background execution, this method is called instead of applicationWillTerminate: when the user quits.
        
        [[UIApplication sharedApplication] beginBackgroundTaskWithExpirationHandler:nil];
    }
    
    - (void)applicationWillEnterForeground:(UIApplication *)application {
        // Called as part of the transition from the background to the inactive state; here you can undo many of the changes made on entering the background.
    }
    
    - (void)applicationDidBecomeActive:(UIApplication *)application {
        // Restart any tasks that were paused (or not yet started) while the application was inactive. If the application was previously in the background, optionally refresh the user interface.
    }
    
    - (void)applicationWillTerminate:(UIApplication *)application {
        // Called when the application is about to terminate. Save data if appropriate. See also applicationDidEnterBackground:.
    }
    
    @end

    2.在视图控制器中添加远程控制事件并音频播放进行控制

    //
    //  ViewController.m
    //  RemoteEvent
    //
    //  Created by Kenshin Cui on 14-3-16.
    //  Copyright (c) 2014年 Kenshin Cui. All rights reserved.
    //
    
    #import "ViewController.h"
    
    @interface ViewController (){
        UIButton *_playButton;
        BOOL _isPlaying;
    }
    
    @end
    
    @implementation ViewController
                
    - (void)viewDidLoad {
        [super viewDidLoad];
        
        [self initLayout];
    }
    
    -(BOOL)canBecomeFirstResponder{
        return NO;
    }
    
    -(void)viewDidAppear:(BOOL)animated{
        [super viewDidAppear:animated];
        _player = [[AVPlayer alloc] initWithURL:[NSURL URLWithString:@"http://stream.jewishmusicstream.com:8000"]];
    
        //[_player play];
        //_isPlaying=true;
    }
    
    #pragma mark 远程控制事件
    -(void)remoteControlReceivedWithEvent:(UIEvent *)event{
        NSLog(@"%i,%i",event.type,event.subtype);
        if(event.type==UIEventTypeRemoteControl){
            switch (event.subtype) {
                case UIEventSubtypeRemoteControlPlay:
                    [_player play];
                    _isPlaying=true;
                    break;
                case UIEventSubtypeRemoteControlTogglePlayPause:
                    if (_isPlaying) {
                        [_player pause];
                    }else{
                        [_player play];
                    }
                    _isPlaying=!_isPlaying;
                    break;
                case UIEventSubtypeRemoteControlNextTrack:
                    NSLog(@"Next...");
                    break;
                case UIEventSubtypeRemoteControlPreviousTrack:
                    NSLog(@"Previous...");
                    break;
                case UIEventSubtypeRemoteControlBeginSeekingForward:
                    NSLog(@"Begin seek forward...");
                    break;
                case UIEventSubtypeRemoteControlEndSeekingForward:
                    NSLog(@"End seek forward...");
                    break;
                case UIEventSubtypeRemoteControlBeginSeekingBackward:
                    NSLog(@"Begin seek backward...");
                    break;
                case UIEventSubtypeRemoteControlEndSeekingBackward:
                    NSLog(@"End seek backward...");
                    break;
                default:
                    break;
            }
            [self changeUIState];
        }
    }
    
    #pragma mark 界面布局
    -(void)initLayout{
        //专辑封面
        UIImage *image=[UIImage imageNamed:@"wxl.jpg"];
        UIImageView *imageView=[[UIImageView alloc]initWithFrame:[UIScreen mainScreen].applicationFrame];
        imageView.image=image;
        imageView.contentMode=UIViewContentModeScaleAspectFill;
        [self.view addSubview:imageView];
        //播放控制面板
        UIView *view=[[UIView alloc]initWithFrame:CGRectMake(0, 480, 320, 88)];
        view.backgroundColor=[UIColor lightGrayColor];
        view.alpha=0.9;
        [self.view addSubview:view];
        
        //添加播放按钮
        _playButton=[UIButton buttonWithType:UIButtonTypeCustom];
        _playButton.bounds=CGRectMake(0, 0, 50, 50);
        _playButton.center=CGPointMake(view.frame.size.width/2, view.frame.size.height/2);
        [self changeUIState];
        [_playButton addTarget:self action:@selector(btnClick:) forControlEvents:UIControlEventTouchUpInside];
        [view addSubview:_playButton];
    }
    
    #pragma mark 界面状态
    -(void)changeUIState{
        if(_isPlaying){
            [_playButton setImage:[UIImage imageNamed:@"playing_btn_pause_n.png"] forState:UIControlStateNormal];
            [_playButton setImage:[UIImage imageNamed:@"playing_btn_pause_h.png"] forState:UIControlStateHighlighted];
        }else{
            [_playButton setImage:[UIImage imageNamed:@"playing_btn_play_n.png"] forState:UIControlStateNormal];
            [_playButton setImage:[UIImage imageNamed:@"playing_btn_play_h.png"] forState:UIControlStateHighlighted];
        }
    }
    
    -(void)btnClick:(UIButton *)btn{
        if (_isPlaying) {
            [_player pause];
        }else{
            [_player play];
        }
        _isPlaying=!_isPlaying;
        [self changeUIState];
    }
    @end

    运行效果(真机截图):

    MusicPlayer

    注意:

    • 为了模拟一个真实的播放器,程序中我们启用了后台运行模式,配置方法:在info.plist中添加UIBackgroundModes并且添加一个元素值为audio。 
    • 即使利用线控进行音频控制我们也无法监控到耳机增加音量、减小音量的按键操作(另外注意模拟器无法模拟远程事件,请使用真机调试)。 
    • 子事件的类型跟当前音频状态有直接关系,点击一次播放/暂停按钮究竟是【播放】还是【播放/暂停】状态切换要看当前音频处于什么状态,如果处于停止状态则点击一下是播放,如果处于暂停或播放状态点击一下是暂停和播放切换。 
    • 上面的程序已在真机调试通过,无论是线控还是点击应用按钮都可以控制播放或暂停。
    展开全文
  • 模式识别

    千次阅读 2019-06-04 21:53:52
    模式可以看作是对象的组成成分或影响因素间存在的规律性关系,或者是因素间存在确定性或随机性规律的对象、过程或事件的集合。 模式识别就是对模式的区分和认识,把对象根据其特征归到若干类别中适当的一类。 模式...

    1.1 模式与模式识别

    模式可以看作是对象的组成成分或影响因素间存在的规律性关系,或者是因素间存在确定性或随机性规律的对象、过程或事件的集合。

    模式识别就是对模式的区分和认识,把对象根据其特征归到若干类别中适当的一类。

    模式识别是通过一系列数学方法让机器(计算机)来实现类似人的模式识别能力。

    样本:所研究对象的一个个体。

    样本集:若干样本的集合。

    类或类别:在所有样本上定义的一个子集,处于同一类的样本在我们所关心的某种性质上是不可区分的,即具有相同的模式。

    特征:指用于表征样本的观测,通常是数值表示的某些量化特征,有时也被称为属性。如果存在多个特征,则它们就组成了特征向量。样本的特征构成了样本的特征空间,空间的维数就是特征的个数,而每一个样本就是特征空间的一个点。某些情况下,对样本的原始描述可能是非数值形式的,此时通常需要采用一定的方法把这些特征转换成数值特征。

    已知样本:指事先知道的类别标号的样本。

    未知样本:指类别标号未知但特征已知的样本。

    所谓模式识别的问题就是用计算机的方法根据样本的特征将样本划分到一定的类别中去。

    1.2 模式识别的主要方法

    解决模式识别问题的方法可以归纳为基于基础知识的方法和基于数据的方法两大类。

    1.3 监督模式识别与非监督模式的识别

    监督模式识别:在要解决的模式是别的问题中,我们已知要划分的类别,并且能够获得一定数量的类别已知的训练样本,这种情况下建立分类器的问题属于监督学习的问题。

    无监督模式识别:根据样本特征将样本聚成几个类,使属于同一个类的样本在一定意义上是相似的,而不同类之间的样本则由较大差异,这种学习过程称为非监督模式学习。

    1.4  模式识别系统举例

    语音识别

    说话人识别

    字符与文字识别

    复杂图像中特定目标的识别

    1.5 模式识别的系统的典型构成

    一个模式识别系统通常包括原始数据的获取和预处理、特征提取与选择,分类或聚类、后处理四个主要部分。

    处理监督模式识别问题的一般步骤:

    分析问题:深入研究应用领域的问题,分析是否属于模式识别问题,把所研究的目标表示为一定的类别,分析给定数据或者可以观测的数据中哪些因素可能与分类有关。

    原始特征获取:设计实验,得到已知样本,对样本实施观测和预处理,获取可能与样本分类有关的观测向量(原始特征)

    特征提取与选择:为了更好地进行分类,可能需要采用一定的算法对特征进行再次提取和选择。

    分类器设计:选择一定的分类器方法,用已知样本进行分类器训练

    分类决策:利用一定的算法对分类器性能进行分类,必要时根据领域知识进行进一步处理。

    处理非监督模式识别问题的一般步骤:

    分析问题:深入研究应用领域的问题,分析研究目标能否通过寻找适当的聚类来达到;如果可能,猜测可能的或希望的类别数目;分析给定数据或者可以观测的数据中哪些因素可能与聚类有关。

    原始特征的获取:设计实验,得到待分析的样本,对样本实施观测和预处理,获取可能与样本聚类有关的观测向量(原始特征)

    特征提取与选择:为了更好地进行聚类,可能需要采用一定的算法对特征进行再次提取和选择。

    聚类分析:选择一定的非监督模式识别方法,用样本进行聚类分析。

    结果解释:考察聚类结果的性能,分析所得聚类与研究目标之间的关系,根据领域知识分析结果的合理性,对聚类的含义给出解释;如果有新样本,把聚类结果用于新样本分类。

     

     

     

     

     

    展开全文
  • 手势识别手势识别将低级别事件处理代码转换成更高一级的行动。它们是对连接至一个视图,它允许,以便向操作进行响应的控制的方式做对象。手势识别解释触摸以确定它们是否对应于特定的手势,诸如滑动,挤压或转动。...
  • jQuery 事件
  • 2017年12月初,在中东的一个秘密地点,计算机使用特殊算法帮助情报分析人员识别由战场上一架小型扫描鹰(ScanEagle)无人机采集的视频中的物体。开始试验的几天,计算机识别出人、汽车、各类建筑等物体的正确率约为...
  • Firefox - 附加软件 - Firebug - Console控制台视图 - console对象输出时,需要注意第一个参数被自动识别为格式字符串 [Console API文档] http://www.getfirebug.com/console.html 在Firebug中,console对象的...
  • 人脸识别研究

    千次阅读 2019-01-14 13:41:54
    一、发展史1、人脸识别的理解: 人脸识别(Face Recognition)是一种依据人的面部特征(如统计或几何特征等),自动进行身份识别的一种生物识别技术,又称为面像识别、人像识别、相貌识别、面孔识别、面部识别等。...
  • 手势识别 技术

    千次阅读 多人点赞 2019-04-19 16:02:27
    手势识别可以分为基于可穿戴设备的识别、基于触摸技术的识别和基于计算机视觉的识别。 一、基于可穿戴设备的识别 1、在手势交互过程中,可以直接采集每根手指的弯曲姿态,通过数据归一化和平滑处理两根手指之间...
  • 人脸识别概要

    千次阅读 2018-04-27 15:20:21
    人脸识别技术,是基于人的脸部特征信息进行身份识别的一种生物识别技术。用摄像机或摄像头采集含有人脸的图像或视频流,并自动在图像中检测和跟踪人脸,进而对检测到的人脸进行脸部的一系列相关技术,通常也叫做人像...
  • 首先给出文档下载地址 : ...1. 应用可以从用户触摸VIew得到事件. 2. 应用可以从用户移动设备得到事件. 3. 应
  • 这是本人第一次写博客,把学到的东西...有教材指出模式是指组成成分或影响因素间存在确定性或随机性规律的对象、过程或事件的集合。而识别是指把对象分门别类地认出来,也有解释是指以前见过,现在再次认出来。简而言
  • JavaScript操作DOM对象 1) JavaScript最终是要操作Html页面,让Html变成DHtml,而操作Html页面就要用到DOM。DOM就是把Html页面模拟成一个对象,就像XDocument一样,把Xml模拟成一个对象,提供了操作各个节点的方法...
  • JavaScript 人脸识别技术

    千次阅读 2014-05-12 13:08:16
    无论是声音识别、人脸识别或其它种识别,人们的外貌、说话的方式都是如此不同,一种图片你可以用不同的方式、从不同的角度拍摄,我不理解这些识别技术是如何做到的。因为之前已经介绍了JavaScript裸体识别技术,...
  • 图像识别初步

    万次阅读 2017-01-05 22:26:07
    本文主要包括以下内容 模式与模式识别的基本概念 过度拟合 最小距离分类器 基于相关的模板匹配 本章的典型案例分析 基于最小距离分类器的鸾尾属植物分类 基于相关技术的图像模式匹配 ...也希望用计
  • 译注:本篇博文主要翻译了QtAssitant中与Qt的元对象系统和事件机制相关的几个重要段落或函数说明,还有一小部分我自己的补充。 Qt类  QtObejctModel:Qt对象模型  T qobject_cast(QObject * object) ,QObject...
  • 刚刚,清华大学的一条重大发现,利用人脸识别技术的漏洞,“ 15分钟解锁19个陌生智能国产手机 ”的事件,引发无数网友关注。 据悉,清华大学的RealAI 团队共选取了 20 款手机,其中1款是国外的,另外19款都是我们...
  • Android 实现人脸识别

    千次阅读 2017-08-30 10:01:46
    基于虹软人脸识别引擎,在Android平台上实现人脸识别功能,即使在离线的情况下依旧运行,不被人采集个人照片的感觉,还是爽爽的。经过整个测试过来,虹软的人脸识别还是很强大的,人脸检测可以控制在20ms之内,人脸...
  • 人脸识别项目总结

    万次阅读 多人点赞 2018-01-01 20:46:51
    这篇博客是记录如何将已有人脸识别算法的前提下,一步步搭建人脸识别系统,找人脸识别算法不用往下看了 作为一水硕,研究生两年就做了一个半成品的人脸识别演示项目,突然想写点什么给以后的自己看看。也许未来自己...
  • 图像识别初探

    万次阅读 2006-03-01 13:24:00
    21.2植物的识别方法. 31.2.1 花与种子的特性. 31.2.2 叶子的特征. 31.3 什么是数字图像处理. 51.3.1 数字图像处理概念及其意义. 51.3.2 数字图像处理中的基本图像类型. 61.3.3 图像格式分析. 7
  • [机器学习]利用KNN算法进行验证码识别

    千次阅读 多人点赞 2017-02-17 12:32:09
    文章:用KNN来进行验证码识别前言对那些及其简单的验证码,可以用KNN算法来破解,这里整理了一个思路,可以了解了解.那么KNN算法原理是什么呢?KNN原理 kNN算法的核心思想是如果一个样本在特征空间中的k个最相邻的样本中...
  • 查指纹、查人脸、查DNA,这些都已成为了公安机关打击违法犯罪...不同的任务和应用会使用不同的声纹识别技术,如缩小刑侦范围时可能需要辨认技术,而银行交易时则需要确认技术。声纹识别就是把声信号转换成电信号,...
  • 非常详细的讲解车牌识别easypr

    万次阅读 多人点赞 2017-11-29 09:27:23
    更新:基于keras-tensorflow的车牌识别,HyperLPR是一个基于Python的使用深度学习针对对中文车牌识别的实现,与开源的EasyPR相比,它的检测速度和鲁棒性和多场景的适应性都要好于EasyPR。 非常详细的讲解车牌...

空空如也

空空如也

1 2 3 4 5 ... 20
收藏数 117,972
精华内容 47,188
关键字:

不同对象能识别不同事件