UITouch - BNR
本节任务:创建一个视图,让用户在视图上拖动手指来画线。
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属性,如下:
@property (nonatomic) CGPoint begin;
@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:方法将不会被调用。
- (void)loadView {
self.view = [[BNRDrawView alloc] initWithFrame:CGRectZero];
}
同时,在实现文件顶部导入BNRDrawView头文件,如下:
#import "BNRDrawView.h"
打开AppDelegate.m文件,创建一个BNRDrawViewController对象,将其作为window的rootViewController。同时导入BNRDrawViewController的头文件, #import "BNRDrawViewController.h" 。
方法application:didFinishLaunchingWithOptions:方法修改如下:
- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions {
// Override point for customization after application launch.
self.window = [[UIWindow alloc] initWithFrame:[[UIScreen mainScreen] bounds]];
BNRDrawViewController *dvc = [[BNRDrawViewController alloc] init];
self.window.rootViewController = dvc; self.window.backgroundColor = [UIColor yellowColor];
[self.window makeKeyAndVisible];
return YES;
}
打开BNRDrawView.m,导入BNRLine头文件,并在类扩展中声明两个属性,如下:
#import "BNRLine.h" @interface BNRDrawView () @property (nonatomic, strong) BNRLine *currentLine;
@property (nonatomic, strong) NSMutableArray *finishedLines; @end
同时,添加initWithFrame:代码如下:
- (instancetype)initWithFrame:(CGRect)r {
self = [super initWithFrame:r];
if (self) {
self.finishedLines = [[NSMutableArray alloc] init];
self.backgroundColor = [UIColor grayColor];
}
return self;
}
同时,需要实现对finishedLine和currentLine的绘制。
当一个视图第一次显示或者一个事件的发生使得视图可见的部分失效了,则drawRect:方法会被调用。绝对不能直接调用该方法。为了使失效的部分重新绘制,需调用setNeedsDisplay或setNeedsDisplayInRect:方法。如下:
- (void)strokeLike:(BNRLine *)line {
UIBezierPath *bp = [UIBezierPath bezierPath];
bp.lineWidth = ;
bp.lineCapStyle = kCGLineCapRound; [bp moveToPoint:line.begin];
[bp addLineToPoint:line.end];
[bp stroke];
} - (void)drawRect:(CGRect)rect {
[[UIColor blackColor] set];
for (BNRLine *line in self.finishedLines) {
[self strokeLike:line];
}
if (self.currentLine) {
[[UIColor redColor] set];
[self strokeLike:self.currentLine];
}
}
该工程的对象关系如下所示:
当触摸事件开始时,需要创建一个line,将begin和end设置为触摸开始的点位置处。当手指移动时,将更新end。因此,打开BNRDrawView.m文件,touchesBegin:withEvent:方法实现如下:
- (void)touchesBegan:(NSSet<UITouch *> *)touches withEvent:(UIEvent *)event {
UITouch *t = [touches anyObject];
CGPoint location = [t locationInView:self];
self.currentLine = [[BNRLine alloc] init];
self.currentLine.begin = location;
self.currentLine.end = location;
[self setNeedsDisplay];
}
touchesMoved:withEvent:方法实现如下:
- (void)touchesMoved:(NSSet<UITouch *> *)touches withEvent:(UIEvent *)event {
UITouch *t = [touches anyObject];
CGPoint location = [t locationInView:self];
self.currentLine.end = location;
[self setNeedsDisplay];
}
当触摸事件结束时,将currentLine添加到finishedLines,touchesEnded:withEvent:实现如下:
- (void)touchesEnded:(NSSet<UITouch *> *)touches withEvent:(UIEvent *)event {
[self.finishedLines addObject:self.currentLine];
self.currentLine = nil;
[self setNeedsDisplay];
}
启动程序,当正在画线时,线条显示红色。当画线结束,将变为黑色。
默认情况下,一个视图将一次只接受一个触摸。
此时,需要一个属性来包含尽可能多的将在屏幕上画出的线条。打开BNRDrawView.m,用下列的属性代替currentLine。
@property (nonatomic, strong) NSMutableDictionary *linesInProgress;
修改BNRDrawView中initWithFrame:方法使其能接收多点触摸,并初始化linesInProgress属性,如下:
- (instancetype)initWithFrame:(CGRect)r {
self = [super initWithFrame:r];
if (self) {
self.linesInProgress = [[NSMutableDictionary alloc] init];
self.finishedLines = [[NSMutableArray alloc] init];
self.backgroundColor = [UIColor grayColor];
self.multipleTouchEnabled = YES;
}
return self;
}
修改触摸响应事件如下:
- (void)touchesBegan:(NSSet<UITouch *> *)touches withEvent:(UIEvent *)event {
NSLog(@"%@", NSStringFromSelector(_cmd)); for (UITouch *t in touches) {
CGPoint location = [t locationInView:self];
BNRLine *line = [[BNRLine alloc] init];
line.begin = location;
line.end = location;
NSValue *key = [NSValue valueWithNonretainedObject:t];
self.linesInProgress[key] = line;
}
[self setNeedsDisplay];
} - (void)touchesMoved:(NSSet<UITouch *> *)touches withEvent:(UIEvent *)event {
NSLog(@"%@", NSStringFromSelector(_cmd)); for (UITouch *t in touches) {
NSValue *key = [NSValue valueWithNonretainedObject:t];
BNRLine *line = self.linesInProgress[key];
line.end = [t locationInView:self];
} [self setNeedsDisplay];
} - (void)touchesEnded:(NSSet<UITouch *> *)touches withEvent:(UIEvent *)event {
NSLog(@"%@", NSStringFromSelector(_cmd)); for (UITouch *t in touches) {
NSValue *key = [NSValue valueWithNonretainedObject:t];
BNRLine *line = self.linesInProgress[key];
[self.finishedLines addObject:line];
[self.linesInProgress removeObjectForKey:key];
}
[self setNeedsDisplay];
}
其中,我们使用valueWithNonretainedObject:方法来产生key用于保存BNRLine。该方法产生的NSValue对象将拥有与该lian有关的UITouch对象的地址。在每次触摸事件中。UITouch对象的地址是保持不变的。
在一个NSDictionary中,object使用的key必须遵守NSCopying协议,该协议允许他们接收copy方法。
最后,修改drawRect:方法:
- (void)drawRect:(CGRect)rect {
[[UIColor blackColor] set];
for (BNRLine *line in self.finishedLines) {
[self strokeLike:line];
}
[[UIColor redColor] set];
for (NSValue *key in self.linesInProgress) {
[self strokeLike:self.linesInProgress[key]];
}
}
最后,添加touchesCancelled:withEvent:方法。当一个触摸事件取消时,应将在linesInProgress中的线条全部移除。如下:
- (void)touchesCancelled:(NSSet<UITouch *> *)touches withEvent:(UIEvent *)event {
NSLog(@"%@", NSStringFromSelector(_cmd));
for (UITouch *t in touches) {
NSValue *key = [NSValue valueWithNonretainedObject:t];
[self.linesInProgress removeObjectForKey:key];
}
[self setNeedsDisplay];
}
程序代码:http://pan.baidu.com/s/1jGKYYt4
UITouch - BNR的更多相关文章
- UIGestureRecognizer - BNR
继续上篇UITouch - BNR.该篇将实现线条选择.移动和删除操作. UIGestureRecognizer有一系列子类,每一个子类都用于识别特定的手势.当识别出一个手势时,手势识别器会拦截视图的 ...
- 你真的了解UIEvent、UITouch吗?
一:首先查看一下关于UIEvent的定义 //事件类型 typedef NS_ENUM(NSInteger, UIEventType) { UIEventTypeTouches, UIEventTyp ...
- iOS开发——UI进阶篇(十二)事件处理,触摸事件,UITouch,UIEvent,响应者链条,手势识别
触摸事件 在用户使用app过程中,会产生各种各样的事件 一.iOS中的事件可以分为3大类型 触摸事件加速计事件远程控制事件 响应者对象在iOS中不是任何对象都能处理事件,只有继承了UIResponde ...
- iOS - UITouch
前言 NS_CLASS_AVAILABLE_IOS(2_0) @interface UITouch : NSObject @available(iOS 2.0, *) public class UIT ...
- UITouch的用法
UITouch一般无法直接获取,是通过UIView的touchesBegan等函数获得. //这四个方法是UIResponder中得方法 // Generally, all responders wh ...
- 触摸事件UITouch的用法
触摸屏幕是iOS设备接受用户输入的主要方式,包括单击.双击.拨动以及多点触摸等,这些操作都会产生触摸事件. 在Cocoa中,代表触摸对象的类是UITouch.当用户触摸屏幕后,就会产生相应的事件,所有 ...
- UITouch 触摸事件处理(实例)
来源:http://www.open-open.com/lib/view/open1341882439838.html 1. UITouch 的主要方法: - (void)touchesBegan:( ...
- UITouch触摸事件
UITouch触摸事件 主要为三个方法 1.-(void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event{2.3. UITouch * ...
- UITouch附加
框架 /System/Library/Frameworks/SpriteKit.framework 可用性 可用于iOS 7.0或者更晚的版本 声明于 SKNode.h 概览 重要提示:这是一个初步的 ...
随机推荐
- 如何解决svn清理失败 不能更新 cleanup失败 cleanup乱码 更新乱码 svn更新提示清理 清理乱码不能清理 svn故障修复SVN cleanup 陷入死循环 svn cleanup时遇到错误怎么办
平时使用svn的过程中,有的时候由于自己操作故障或者系统原因,导致svn不能更新,提示cleanup也不能成功,陷入了死循环 原因是;svn的数据库队列原因 1,下载sqlite3.exe,googl ...
- JDK动态代理深入理解分析并手写简易JDK动态代理(下)
原文同步发表至个人博客[夜月归途] 原文链接:http://www.guitu18.com/se/java/2019-01-05/27.html 作者:夜月归途 出处:http://www.guitu ...
- docker 创建mysql镜像,并成功进行远程连接
1.安装 1.1 拉取镜像 docker pull mysql 拉取成功可以验证一下 docker images 1.2 创建并启动一个mysql容器 docker run --name ly-mys ...
- C# 给现有PDF文档添加页眉、页脚
概述 页眉页脚是一篇完整.精致的文档的重要组成部分.在页眉页脚处,可以呈现的内容很多,如公司名称.页码.工作表名.日期.图片,如LOGO.标记等.在之前的文章中介绍了如何通过新建一页空白PDF页来添加 ...
- 设计模式之代理模式(Proxy)(2)
代理模式是为其他对象提供一种代理以控制对这个对象的访问.在某些情况下,一个对象不适合或者不能直接引用另一个对象,而代理对象可以在客户端和目标对象之间起到中介的作用,其特征是代理类与委托类有同样的接口. ...
- 小程序多端框架全面测评:chameleon、Taro、uni-app、mpvue、WePY
摘要: 微信小程序开发技巧. 作者:coldsnap 原文:小程序多端框架全面测评 Fundebug经授权转载,版权归原作者所有. 最近前端届多端框架频出,相信很多有代码多端运行需求的开发者都会产生一 ...
- 统计字符串中字符出现的次数(||和&&的区别)
var str = "ProsperLee"; // || 返回第一个为真的表达式的值,若全为假则返回最后一个表达式的值 // && 返回第一个为假的表达式的值,若 ...
- Net中获取程序集路径
从内存中加载的程序集,无路径 IIS中路径 protected void Page_Load(object sender, EventArgs e) { Response.Write(&quo ...
- 新版的nuget包 PackageLicense 这样写
Intro 最近编译类库项目的时候发现总是有个 licenseUrl 的警告,警告信息如下: warning NU5125: The 'licenseUrl' element will be depr ...
- vue父子组件之间传值
vue父子组件进行传值 vue中的父子组件,什么是父组件什么是子组件呢?就跟html标签一样,谁包裹着谁谁就是父组件,被包裹的元素就是子组件. 父组件向子组件传值 下面用的script引入的方式,那种 ...