本节任务:创建一个视图,让用户在视图上拖动手指来画线。  


  UIView类能够重载4个方法来处理不同的触摸事件。

- (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   //系统事件,在触摸结束前被其打断。

@property(nonatomic, getter=isMultipleTouchEnabled) BOOL multipleTouchEnabled   // 默认为NO,如果想要处理多手指触摸事件,则需设置为YES。

  一个UITouch对应在屏幕上的一个手指。如果一个视图开始拥有触摸对象,该视图将拥有触摸对象的生命周期。你不应该引用触摸对象。  


  创建一个新的Single View Application工程,命名为TouchTracker。

  首先,需要一个模型对象来描述line。创建一个NSObject的子类,取名为BNRLine。

  再创建一个UIView的子类,取名为BNRDrawView。BNRDrawView用来追踪全部画好了的线条以及目前正在画的线条。

  再创建一个UIViewController的子类来管理BNRDrawView对象,取名为BNRDrawViewController。

  

  在BNRLine.h头文件中,声明两个CGPoint属性,如下:

  1. @property (nonatomic) CGPoint begin;
  2. @property (nonatomic) CGPoint end;

同时,将 #import <Foundation/Foundation.h> 改为 #import <UIKit/UIKit.h> 。

  打开BNRDrawViewController.m文件,重载loadView方法,将BNRDrawView对象设置为BNRDrawViewController的view。

当请求显示控制器的view时,但是为nil,则视图控制器会自动调用loadView方法。该方法应加载或创建一个视图,并将其赋给控制器的view属性。如果视图控制器有一个相对于的nib文件,loadView方法将从该nib文件加载view。如果你使用了Interface Builder创建了视图并初始化了视图控制器,你一定不能重载该方法。

  如果使用Interface Builder设计用户界面时,当视图对象从nib文件中加载视图时,initWithFrame:方法将不会被调用。

  1. - (void)loadView {
  2. self.view = [[BNRDrawView alloc] initWithFrame:CGRectZero];
  3. }

同时,在实现文件顶部导入BNRDrawView头文件,如下:

  1. #import "BNRDrawView.h"

  打开AppDelegate.m文件,创建一个BNRDrawViewController对象,将其作为window的rootViewController。同时导入BNRDrawViewController的头文件, #import "BNRDrawViewController.h" 。

方法application:didFinishLaunchingWithOptions:方法修改如下:

  1. - (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions {
  2. // Override point for customization after application launch.
  3. self.window = [[UIWindow alloc] initWithFrame:[[UIScreen mainScreen] bounds]];
  4. BNRDrawViewController *dvc = [[BNRDrawViewController alloc] init];
  5. self.window.rootViewController = dvc;
  6.  
  7. self.window.backgroundColor = [UIColor yellowColor];
  8. [self.window makeKeyAndVisible];
  9. return YES;
  10. }

  打开BNRDrawView.m,导入BNRLine头文件,并在类扩展中声明两个属性,如下:

  1. #import "BNRLine.h"
  2.  
  3. @interface BNRDrawView ()
  4.  
  5. @property (nonatomic, strong) BNRLine *currentLine;
  6. @property (nonatomic, strong) NSMutableArray *finishedLines;
  7.  
  8. @end

同时,添加initWithFrame:代码如下:

  1. - (instancetype)initWithFrame:(CGRect)r {
  2. self = [super initWithFrame:r];
  3. if (self) {
  4. self.finishedLines = [[NSMutableArray alloc] init];
  5. self.backgroundColor = [UIColor grayColor];
  6. }
  7. return self;
  8. }

同时,需要实现对finishedLine和currentLine的绘制。

当一个视图第一次显示或者一个事件的发生使得视图可见的部分失效了,则drawRect:方法会被调用。绝对不能直接调用该方法。为了使失效的部分重新绘制,需调用setNeedsDisplay或setNeedsDisplayInRect:方法。如下:

  1. - (void)strokeLike:(BNRLine *)line {
  2. UIBezierPath *bp = [UIBezierPath bezierPath];
  3. bp.lineWidth = ;
  4. bp.lineCapStyle = kCGLineCapRound;
  5.  
  6. [bp moveToPoint:line.begin];
  7. [bp addLineToPoint:line.end];
  8. [bp stroke];
  9. }
  10.  
  11. - (void)drawRect:(CGRect)rect {
  12. [[UIColor blackColor] set];
  13. for (BNRLine *line in self.finishedLines) {
  14. [self strokeLike:line];
  15. }
  16. if (self.currentLine) {
  17. [[UIColor redColor] set];
  18. [self strokeLike:self.currentLine];
  19. }
  20. }

该工程的对象关系如下所示:


  当触摸事件开始时,需要创建一个line,将begin和end设置为触摸开始的点位置处。当手指移动时,将更新end。因此,打开BNRDrawView.m文件,touchesBegin:withEvent:方法实现如下:

  1. - (void)touchesBegan:(NSSet<UITouch *> *)touches withEvent:(UIEvent *)event {
  2. UITouch *t = [touches anyObject];
  3. CGPoint location = [t locationInView:self];
  4. self.currentLine = [[BNRLine alloc] init];
  5. self.currentLine.begin = location;
  6. self.currentLine.end = location;
  7. [self setNeedsDisplay];
  8. }

  touchesMoved:withEvent:方法实现如下:

  1. - (void)touchesMoved:(NSSet<UITouch *> *)touches withEvent:(UIEvent *)event {
  2. UITouch *t = [touches anyObject];
  3. CGPoint location = [t locationInView:self];
  4. self.currentLine.end = location;
  5. [self setNeedsDisplay];
  6. }

  当触摸事件结束时,将currentLine添加到finishedLines,touchesEnded:withEvent:实现如下:

  1. - (void)touchesEnded:(NSSet<UITouch *> *)touches withEvent:(UIEvent *)event {
  2. [self.finishedLines addObject:self.currentLine];
  3. self.currentLine = nil;
  4. [self setNeedsDisplay];
  5. }

  启动程序,当正在画线时,线条显示红色。当画线结束,将变为黑色。


  默认情况下,一个视图将一次只接受一个触摸。

  此时,需要一个属性来包含尽可能多的将在屏幕上画出的线条。打开BNRDrawView.m,用下列的属性代替currentLine。

  1. @property (nonatomic, strong) NSMutableDictionary *linesInProgress;

修改BNRDrawView中initWithFrame:方法使其能接收多点触摸,并初始化linesInProgress属性,如下:

  1. - (instancetype)initWithFrame:(CGRect)r {
  2. self = [super initWithFrame:r];
  3. if (self) {
  4. self.linesInProgress = [[NSMutableDictionary alloc] init];
  5. self.finishedLines = [[NSMutableArray alloc] init];
  6. self.backgroundColor = [UIColor grayColor];
  7. self.multipleTouchEnabled = YES;
  8. }
  9. return self;
  10. }

修改触摸响应事件如下:

  1. - (void)touchesBegan:(NSSet<UITouch *> *)touches withEvent:(UIEvent *)event {
  2. NSLog(@"%@", NSStringFromSelector(_cmd));
  3.  
  4. for (UITouch *t in touches) {
  5. CGPoint location = [t locationInView:self];
  6. BNRLine *line = [[BNRLine alloc] init];
  7. line.begin = location;
  8. line.end = location;
  9. NSValue *key = [NSValue valueWithNonretainedObject:t];
  10. self.linesInProgress[key] = line;
  11. }
  12. [self setNeedsDisplay];
  13. }
  14.  
  15. - (void)touchesMoved:(NSSet<UITouch *> *)touches withEvent:(UIEvent *)event {
  16. NSLog(@"%@", NSStringFromSelector(_cmd));
  17.  
  18. for (UITouch *t in touches) {
  19. NSValue *key = [NSValue valueWithNonretainedObject:t];
  20. BNRLine *line = self.linesInProgress[key];
  21. line.end = [t locationInView:self];
  22. }
  23.  
  24. [self setNeedsDisplay];
  25. }
  26.  
  27. - (void)touchesEnded:(NSSet<UITouch *> *)touches withEvent:(UIEvent *)event {
  28. NSLog(@"%@", NSStringFromSelector(_cmd));
  29.  
  30. for (UITouch *t in touches) {
  31. NSValue *key = [NSValue valueWithNonretainedObject:t];
  32. BNRLine *line = self.linesInProgress[key];
  33. [self.finishedLines addObject:line];
  34. [self.linesInProgress removeObjectForKey:key];
  35. }
  36. [self setNeedsDisplay];
  37. }

  其中,我们使用valueWithNonretainedObject:方法来产生key用于保存BNRLine。该方法产生的NSValue对象将拥有与该lian有关的UITouch对象的地址。在每次触摸事件中。UITouch对象的地址是保持不变的。

  在一个NSDictionary中,object使用的key必须遵守NSCopying协议,该协议允许他们接收copy方法。

最后,修改drawRect:方法:

  1. - (void)drawRect:(CGRect)rect {
  2. [[UIColor blackColor] set];
  3. for (BNRLine *line in self.finishedLines) {
  4. [self strokeLike:line];
  5. }
  6. [[UIColor redColor] set];
  7. for (NSValue *key in self.linesInProgress) {
  8. [self strokeLike:self.linesInProgress[key]];
  9. }
  10. }

  最后,添加touchesCancelled:withEvent:方法。当一个触摸事件取消时,应将在linesInProgress中的线条全部移除。如下:

  1. - (void)touchesCancelled:(NSSet<UITouch *> *)touches withEvent:(UIEvent *)event {
  2. NSLog(@"%@", NSStringFromSelector(_cmd));
  3. for (UITouch *t in touches) {
  4. NSValue *key = [NSValue valueWithNonretainedObject:t];
  5. [self.linesInProgress removeObjectForKey:key];
  6. }
  7. [self setNeedsDisplay];
  8. }

程序代码:http://pan.baidu.com/s/1jGKYYt4

UITouch - BNR的更多相关文章

  1. UIGestureRecognizer - BNR

    继续上篇UITouch - BNR.该篇将实现线条选择.移动和删除操作. UIGestureRecognizer有一系列子类,每一个子类都用于识别特定的手势.当识别出一个手势时,手势识别器会拦截视图的 ...

  2. 你真的了解UIEvent、UITouch吗?

    一:首先查看一下关于UIEvent的定义 //事件类型 typedef NS_ENUM(NSInteger, UIEventType) { UIEventTypeTouches, UIEventTyp ...

  3. iOS开发——UI进阶篇(十二)事件处理,触摸事件,UITouch,UIEvent,响应者链条,手势识别

    触摸事件 在用户使用app过程中,会产生各种各样的事件 一.iOS中的事件可以分为3大类型 触摸事件加速计事件远程控制事件 响应者对象在iOS中不是任何对象都能处理事件,只有继承了UIResponde ...

  4. iOS - UITouch

    前言 NS_CLASS_AVAILABLE_IOS(2_0) @interface UITouch : NSObject @available(iOS 2.0, *) public class UIT ...

  5. UITouch的用法

    UITouch一般无法直接获取,是通过UIView的touchesBegan等函数获得. //这四个方法是UIResponder中得方法 // Generally, all responders wh ...

  6. 触摸事件UITouch的用法

    触摸屏幕是iOS设备接受用户输入的主要方式,包括单击.双击.拨动以及多点触摸等,这些操作都会产生触摸事件. 在Cocoa中,代表触摸对象的类是UITouch.当用户触摸屏幕后,就会产生相应的事件,所有 ...

  7. UITouch 触摸事件处理(实例)

    来源:http://www.open-open.com/lib/view/open1341882439838.html 1. UITouch 的主要方法: - (void)touchesBegan:( ...

  8. UITouch触摸事件

    UITouch触摸事件 主要为三个方法 1.-(void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event{2.3. UITouch * ...

  9. UITouch附加

    框架 /System/Library/Frameworks/SpriteKit.framework 可用性 可用于iOS 7.0或者更晚的版本 声明于 SKNode.h 概览 重要提示:这是一个初步的 ...

随机推荐

  1. MVC Scaffolding SmartCode-Engine 更新

    概述 通过扩展visual studio.net scaffolding组件,添加了一套功能完善的代码模板,包括Controller,Model,View,Businessd等各种功能的代码,配合En ...

  2. jQuery点击图片放大拖动查看效果

    效果如图: 放大前: 放大后(可拖动图片浏览): 源码如下: <html xmlns="http://www.w3.org/1999/xhtml"> <head& ...

  3. Java开发笔记(二十二)神奇的冒号

    Java中的标点符号主要有两类用途,一类是运算符,包括加号+.减号-.乘号*.除号/.取余号%.等号=.大于号>.小于号<.与号&.或号|.非号!.异或号^等等,另一类则是分隔符, ...

  4. Kotlin for循环使用

    普通for循环 for(i in 1..4){ println(i) } 结果为1234 循环四次 反序for循环 for(i in 4 downTo 1){ println(i) } 结果为4321 ...

  5. ajaxFileUpload onchang上传文件插件第二次失效刷新一次才能再次调用触发change事件

    关于用ajaxfileupload时,遇到一个要刷新一次页面才能再次上传, ajaxFileUpload 用onchang上传只能上传一次 第二次就失效了 我找这个问题找了很长时间 ajaxFileU ...

  6. 页面内容不够高footer始终位于页面的最底部

    相信很多前端工程师在开发页面时会遇到这个情况:当整个页面高度不足以占满显示屏一屏,页脚不是在页面最底部,用户视觉上会有点不好看,想让页脚始终在页面最底部,我们可能会想到用: 1.min-height来 ...

  7. 手机端两端对齐,兼容ios,安卓

    .div-title p label{ text-align: justify; width: 18%; display: inline-block; text-align-last: justify ...

  8. 后台返回excel文件流,js下载

    /** 下载excel */ downloadExcel(data: Blob): void { var blob = new Blob([data], { type: 'application/vn ...

  9. 开源ERP-成功案例分析(3)

    Odoo用户概要 关于Odoo全球的用户,我们来看一些数据: Odoo目前全球有300万使用者 Odoo系统上每天新创建的数据库超过1000个 Odoo和Word.Excel.PowerPoint一样 ...

  10. vue父子组件之间传值

    vue父子组件进行传值 vue中的父子组件,什么是父组件什么是子组件呢?就跟html标签一样,谁包裹着谁谁就是父组件,被包裹的元素就是子组件. 父组件向子组件传值 下面用的script引入的方式,那种 ...