[apple文档]事件编程指南

一.事件的类型和传递

1.UIEventType(Touches,Motion,RemoteControl),UIEventSubType,

2.touch event的传递规则:touchTest,pointInside:withEvent.

以下是我的一些分析:可能有出入

baseView中连续添加viewA,viewB,viewC,viewA中添viewD,viewD中添加viewE;

先测试hittest:

例子A:viewA,viewB,viewC都实现了 hittest,假设直接返回self,则点击baseView任何地方,则响应viewC,因为是第一层的最上层,如果viewC的hittest返回nil,则传递给viewB,则viewB响应,以此类推。当然你可以再对应方法中调用super hitTest方法,这样的话就是系统默认的方式去判断返回的view是哪个,如果不实现pointInView,则是返回点中的view.顺便说一句,如果baseView实现hitTest并且返回self,则直接响应baseView了,返回nil,则结束了,不会去自动寻找下层。hitTest是先寻找当前层,再寻找下一层的。此外假设viewA超过了baseView,则点击baseView外地方,则没有任何响应。点击viewA也是。

例子B:viewA,viewB,viewC都实现 hitTest并调用super hitTest或者干脆就不实现,viewD,viewE实现了pointInside方法,当viewD返回true时,则viewE也会触发以此pointInside方法,如果返回YES,则viewE触发touch,返回NO,则viewD触发touch,如果viewD方法中返回NO,则viewA触发了touch。

结论1:调用super hitTest其实就是按照事件传递的基本规则去寻找事件的响应者,在你自己实现了的方法中不调用super方法,则不会去传递到下一层,所以当baseView实现hitTest但是返回nil则没有调用subview的hittest,但是如果viewC返回nil,则还会调用viewB,因为当baseView没有实现hitTest方法时,则会默认执行[super hittest].

结论2:pintInside和hitTest传递的方式都是从上层传递到下层的捕获方式的传递,当pointInside返回yes,则会判断其子view是否返回yes,如果有,则再继续寻找,直到都返回no,或者返回yes,并且已经是最后一层,才能确定该view是触发view.而hitTest则是调用super hitTest方法来触发这样的传递,如果你返回的view不是你想要的view,还可以决定其他view来作为触发的view,反正最后关键是看方法返回的是哪个UIView作为触发对象。

3.first responder是指第一个处理应用事件的对象(通常是UIView),这些事件不仅仅是touch事件

a)Motion events

b)remote-control events

c)Action messages:用户在应用中创建了一个control(button或者slider),并且没有为其指定target.

d)editing-menu message:

e)text-editing:UIKit会自动将text field或者text view 设置成first responder

二.多触摸事件

1.UIEvent提供了整个事件过程中的touch事件

a)allTouches

b)touchesForWindow:返回针对某个window的touch对象

c)touchesForView:返回针对某个view的touch对象

2.UITouch某个touch事件

a)anyObject:返回一个touch对象,NSSet是一个无序的集合,他是用hashcode来保存数据的,不同于NSArray

b)locationInView,previousLocationView:传递self的话,则是返回这个view中的位置

c)tapCount:

d)timstamp

3.touch相关的一些方法

a)userInteractionEnabled

b)UIApplication的beginIgnoringInteractionEvents和endIgnoringInteractionEvents

c)multipleTouchEnabled

d)exclusiveTouch

e)hitTest:withEvent来限制多点触摸事件

4.处理复杂的多触摸事件

a)multipleTouchEnabled=YES

b)要用Core Foundation Dictionary (CFDictionaryRef)来跟踪touches以你为UITouch是没有遵循NSCopying协议,所以不能使用NSDictionary

if ([touches count] > 0) {
        for (UITouch *touch in touches) {
            CGPoint *point = (CGPoint *)CFDictionaryGetValue(touchBeginPoints, touch);
            if (point == NULL) {
                point = (CGPoint *)malloc(sizeof(CGPoint));
                CFDictionarySetValue(touchBeginPoints, touch, point);
            }
            *point = [touch locationInView:view.superview];
        }
    }

从touchBeginPoints查看是否有touch为key的value,如果没有,则创建它,然后在touch上设置该point指针,注意这个是指针,真正赋值的是*point=[touch……],注意给赋值的时候是需要加一个*来赋值。

5.如何判断多触摸事件的结束

- (void)touchesEnded:(NSSet*)touches withEvent:(UIEvent*)event {
    if ([touches count] == [[event touchesForView:self] count]) {
        // last finger has lifted....
    }
}
6.转发touch events,sendEvent(这个可能需要进一步获取资料)

7.其他一些注意点有助于处理触摸事件

a)总是实现event-cancel method,因为你的真个触摸事件流程中可能都会遇到cancel的情况,比方说接到一个邮件或者call.你所要做的就是恢复之前的状态

b)如果你的event方法是是现在一个UIView,UIViewController或者很少见的UIResponder的子类的话,你需要实现所有touch方法,不要调用super方法

如果是其他UIKit responder class,比如UIImageView,UISlider,则不用实现所有touch方法,但是需要调用super对应的方法

c)不要往其它UIKit框架对象转发事件,而是只有你自己的custom View of UIView

d)所有事件方法中不要涉及绘制部分,只需要标记状态,所有绘制代码应该让drawRect来帮助实现

三.手势

1.在发送touch事件的结束阶段(Ended Phase)到hit-test view前,window对象会将它发送到手势识别器所涉及的view及其subviews

2.locationInView:和locationOfTouch:inView

3.处理多个手势并存的情况

a)接受另外一个手势前需要另外一个手势失败:

比如说单击手势是需要双击手势确定失败后才执行:[singleTap requireGestureRecognizerToFail:doubleTap];

b)通过分析多触摸touch事件来阻止某些手势识别器

UIGestureRecognizerDelegate里

gestureRecognizerShouldBegin:当一个手势识别器准备将状态标识为UIGestureRecognizerStatePossible前调用,如果返回NO,则标记为失败

gestureRecognizer:shouldReceiveTouch:这个方法是在window调用touchesBegan事件前调用,返回NO,可以阻止guesture接收这些touch事件

此外还有在UIGestureRecognizerSubclass.h中的2个方法类似

-(BOOL)canPreventGestureRecognizer:(UIGestureRecognizer*)preventedGestureRecognizer;

-(BOOL)canBePreventedByGestureRecognizer:(UIGestureRecognizer*)preventingGestureRecognizer;

c)允许多个手势识别

UIGestureRecognizerDelegate里gestureRecognizer:shouldRecognizeSimultaneouslyWithGestureRecognizer:

4.touches事件和手势事件的流程

假设一个2指的操作:

a)发送2个began phase touch事件给识别器,但是识别器不认,则发送给UIView.

b)发送2个move phase touch事件给识别器,识别器依旧无法识别,然后发给UIView.

c)一个手指离开view,发送一个touch事件给ended phase touch事件给识别器,由于识别器资料不够,还是不能确认,不过这个事件被暂时阻止传送给UIView

d)第二个手指离开,又发送了个touch事件,识别器识别出了手势,将状态标记成UIGuestureRecognizerStateRecognized了,view会接受touchesCancelled:withEvent:

假设最后一步识别器没有识别出,则会将2个end phase touch事件发给touchesEnded:withEvent:方法

此外如果是一个连续的手势识别,则这个状态可能会被标识为UIGuestureRecognizerStateBegan,然后所有事件都会发送给手势识别器,而不发送给view了

影响事件的几个参数:

cancelsTouchesInView(默认为YES):NO的话,手势识别失败的话,也不会调用cancel,那么之前的touch事件也不会是无效的了

delaysTouchesBegan(默认为NO) 在手势识别结束前,不会有任何手势事件发送给view

delaysTouchesEnded(默认为YES)如果是NO,原本view接受的顺序是touchesBegan,touchesEnded,touchesBegan,touchesCancelled;YES的话则是touchesBegan,touchesBegan,touchesCancelled,touchesEnded

5.创建自定义手势识别

手势状态的变化(不连续的(如tap),连续的(如pan)),详细代码可以看Event handling Guide或者《iphone4开发基础》。

四.摇动事件

1.声明是first Responder

- (BOOL)canBecomeFirstResponder {
    return YES;
}
 
- (void)viewDidAppear:(BOOL)animated {
    [self becomeFirstResponder];
}

2.实现代理方法

- (void)motionBegan:(UIEventSubtype)motion withEvent:(UIEvent *)event
{
}
 
- (void)motionEnded:(UIEventSubtype)motion withEvent:(UIEvent *)event
{
    [UIView beginAnimations:nil context:nil];
    [UIView setAnimationDuration:0.5];
    self.view.transform = CGAffineTransformIdentity;
 
    for (UIView *subview in self.view.subviews) {
        subview.transform = CGAffineTransformIdentity;
    }
    [UIView commitAnimations];
 
    for (TransformGesture *gesture in [window allTransformGestures]) {
        [gesture resetTransform];
    }
}
 
- (void)motionCancelled:(UIEventSubtype)motion withEvent:(UIEvent *)event
{
}
3.此外我在开发相关程序时以为这个一定要作为rootcontroller才能响应,其实不是的,假设我有一个rootViewController,然后rootViewController里显示了其他controller的View,那么那个controller同样可以成为第一响应者,只是如果该controller里如果不实现motion方法,则会传递上去,那么就会调用rootViewController的motion方法。

4.coreMotion的使用,详细看文档或者看《iphone4开发基础》
大致就是使用陀螺仪,表示方向,加速记表示偏移量。该类服务的使用可以分为推送和主动获取方法。

五.远程控制多媒体

1.声明first responder

- (void)viewDidAppear:(BOOL)animated {
    [super viewDidAppear:animated];
    [[UIApplication sharedApplication] beginReceivingRemoteControlEvents];
    [self becomeFirstResponder];
}
- (void)viewWillDisappear:(BOOL)animated {
    [[UIApplication sharedApplication] endReceivingRemoteControlEvents];
    [self resignFirstResponder];
    [super viewWillDisappear:animated];
}

- (void) remoteControlReceivedWithEvent: (UIEvent *) receivedEvent {
 
    if (receivedEvent.type == UIEventTypeRemoteControl) {
 
        switch (receivedEvent.subtype) {
 
            case UIEventSubtypeRemoteControlTogglePlayPause:
                [self playOrStop: nil];
                break;
 
            case UIEventSubtypeRemoteControlPreviousTrack:
                [self previousTrack: nil];
                break;
 
            case UIEventSubtypeRemoteControlNextTrack:
                [self nextTrack: nil];
                break;
 
            default:
                break;
        }
    }
}





你可能感兴趣的:([apple文档]事件编程指南)