12章 Touch Events and UIResponder
触摸事件touch events,手势 gestures, 调试 debugging application
UIView 继承UIResponder,所以可以重写以下四个方法:
touchBegan:(NSSet *)touches withEvent:
touchMoved:withEvent:
touchEnded:withEvent:
touchCancel:withEvent:
touche中有个UITouch的对象,包含了触碰的位置等信息
你不需要保存UITouch的对象,因为在事件中你可以访问得到。
当手指滑出初始触碰到视图,会受到touchMoved和touchEnded
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
// 重写几个touch事件,获取location。
// setNeedDisplay,系统自动调用drawRect,
// 如何绘制、设置颜色、stoke等。
#import "BNRDrawView.h"
#import "BNRLine.h"
@interface BNRDrawView()
@property (nonatomic,strong) NSMutableArray *finishedLines;
@property (nonatomic,strong) BNRLine *currentLine;
@end
@implementation BNRDrawView
-(instanceType)initWithFrame:(CGRect)r
{
self = [super initWithFrame:r];
if(self)
{
self.finishedLines = [[NSMutableArray alloc] init];
self.backgroundColor = [UIColor grayColor];
}
}
- (void) strokeLine: (BNRLine *)line
{
UIBezierPath *bp = [UIBezierPath bezierPath];
bp.lineWith = 10;
bp.lineCapStyle = kCGLineCapRound;
[bp moveToPoint:line.begin];
[bp addLineToPoint: line.end];
[bp stroke];
}
-(void)drawRect:(CGRect) rect
{
// 每次都要重新遍历finishedLines再重新绘制???
[[UIColor blackColor] set];
for(BNRLine *line in self.finishedLines]
{
[self strokeLine:line];
}
if(self.currentLine){
[[UIColor redColor] set];
[self strokeLine:self.currentLine];
}
}
- (void) touchBegan:(NSSet *)touches withEvent:(UIEvent *)event
{
UITouch *touch = [touches anyObject];
CGPoint location = [touch locationInView:self];
self.currentLine = [[BNRLine alloc] init];
self.currentLine.begin = location;
self.currentLine.end = location;
[self setNeedDisplay];
}
- (void) touchMoved:(NSSet *)touches withEvent:(UIEvent *)event
{
UITouch *t = [touches anyObject];
CGPoint location = [t locationInView:self];
self.currentLine.end = location;
[self setNeedDisplay];
}
-(void) touchEnded:(NSSet *)touches withEvent:(UIEvent *)event
{
[self.finishedLines addObject:self.currentLine];
self.currentLine = nil;
[self setNeedDisplay];
}
@end
-----------------------------------------------------------------------
多点触控。
默认情况下一个view只能接受一个finger的touch: 一个手指触发了touchBegan,但是没有ended。则view会忽略其他触碰
self.multipleTouchEnabled = YES;
touchCancelled发生在系统事件,比如电话来了~,一般这时候会把当前线段都移除掉
保存多个数据
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;
}
//用NSValue *key来作为字典的key而不是直接用t,因为字典的key是实现了NSCopying协议
------------------------------------------------------------------------
#import "BNRDrawView.h"
#import "BNRLine.h"
@interface BNRDrawView
{
@property (nonatomic,strong) NSMutableDictionary *linesInProgress;
@property (nonatomic,strong) NSMutableArray *finishedLines;
// @property (nonatomic,strong) BNRLine *currentLine;
}
@end
@implementation BNRDrawView
-(instanceType)initWithFrame:(CGRect)r
{
self = [super initWithFrame:r];
if(self)
{
self.linesInProgress = [[NSMutableArray] alloc] init];
self.finishedLines = [[NSMutableArray alloc] init];
self.backgroundColor = [UIColor grayColor];
}
}
- (void) strokeLine: (BNRLine *)line
{
UIBezierPath *bp = [UIBezierPath bezierPath];
bp.lineWith = 10;
bp.lineCapStyle = kCGLineCapRound;
[bp moveToPoint:line.begin];
[bp addLineToPoint: line.end];
[bp stroke];
}
-(void)drawRect:(CGRect) rect
{
// finished
[[UIColor blackColor] set];
for(BNRLine *line in self.finishedLines]
{
[self strokeLine:line];
}
// current progress
[[UIColor redColor] set];
for(BNRLine *line in self.linesInProgress){
[self strokeLine:line];
}
}
- (void) touchBegan:(NSSet *)touches withEvent:(UIEvent *)event
{
for(UITouch *t in touches){
CGPoint location = [t locationInView:self];
NSValue *key = [NSValue valueWithNonretainedObject:t];
BNRLine *line = [[BNRLine alloc] init];
line.begin = location;
line.end = location;
self.linesInProgress[key] = line;
}
[self setNeedDisplay];
}
- (void) touchMoved:(NSSet *)touches withEvent:(UIEvent *)event
{
for(UITouch *t in touches){
CGPoint location = [t locationInView:self];
NSValue *key = [NSValue valueWithNonretainedObject:t];
BNRLine *line = self.linesInProgress[key];
line.end = location;
}
[self setNeedDisplay];
}
-(void) touchEnded:(NSSet *)touches withEvent:(UIEvent *)event
{
for(UITouch *t in touches){
NSValue *key = [NSValue valueWithNonretainedObject:t];
BNRLine *line = self.linesInProgress[key];
[self.finishedLines addObject:line];
[self.linesInProgress removeObjectForKey:key];
}
[self setNeedDisplay];
}
-(void) touchCancelled:(NSSet *)touches withEvent:(UIEvent *)event
{
for(UITouch *t in touches){
NSValue *key = [NSValue valueWithNonretainedObject:t];
[self.linesInProgress removeObjectForKey:key];
}
[self setNeedDisplay];
}
@end
Responder chain
每个responder有一个nextResponder属性,构成chain。 rootView: window's nextResponder is UIApplication
'
UIControl:can set targets and actions
the superclass of UIButton,UISlider...
[rButton addTarget:tempController action:@selector(resetTemperature:) forControlEvents:UIControlEventTouchUpInSide | UIControlEventTouchUpOutSide]
then you can send event:
[self sendActionsForControlEvents:UIControlEventTouchUpInSide]
消息不是直接发送给目标view,而是通过UIApplication来找到target
-----------重写UIResponder---------------------
- (void)touchesEnded:(NSSet *)touches withEvent:(UIEvent *)event
{
// Reference to the touch that is ending
UITouch *touch = [touches anyObject];
// Location of that point in this control's coordinate system
CGPoint touchLocation = [touch locationInView:self];
// Is that point still in my viewing bounds?
if (CGRectContainsPoint(self.bounds, touchLocation))
{
// Send out action messages to all targets registered for this event!
[self sendActionsForControlEvents:UIControlEventTouchUpInside];
} else {
// The touch ended outside the bounds, different control event
[self sendActionsForControlEvents:UIControlEventTouchUpOutside];
}
}