作为事件处理部分,MoveMeView类执行下面的操作:
- 当触摸第一次发生,检查事件发生的位置。
- 双击Welcome按钮外的位置,更新按钮显示的字符串。
- 单击按钮,把按钮的中心移到手指之下,并引发一个动画放大按钮。
- 其他的触摸都忽略。
- 如果手指在按钮之内移动,按钮的位置就会更新以匹配手指。
- 如果手指在按钮内,然后离开设备的表面,按钮以动画的形式回到他的原始位置。
列表5展示MoveMeView类的 touchesBegan:withEvent:方法。手指首次触摸设备的时候系统调用这个方法。方法获得所有的触摸,然后抽出唯一的触摸以及被触摸的物体。UITouch对象中的信息可以说明触摸发生在哪个视图(MoveMeView或者PlacardView)上,以及与之相关的触碰数量。如果触摸是按钮外的双击,touchesBegan:withEvent:方法调用setupNextDisplayString方法改变按钮的欢迎字符串。如果事件发生在Welcome按钮内,使用animateFirstTouchAtPoint:方法放大按钮,并把按钮中心移动到触摸发生的位置上。其他触摸相关的事件都被忽略了。
列表5 处理初步的触摸事件
- (void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event { // We only support single touches, so anyObject // retrieves just that touch from touches UITouch *touch = [touches anyObject]; // Only move the placard view if the touch was in the placard view if ([touch view] != placardView) { // In case of a double tap outside the placard view, // update the placard's display string if ([touch tapCount] == 2) { [placardView setupNextDisplayString]; } return; } // Animate the first touch CGPoint touchPoint = [touch locationInView:self]; [self animateFirstTouchAtPoint:touchPoint]; }
列表6展示MoveMeView类的touchesMoved:withEvent:方法。系统在响应手指已经触摸设备,而且从原始位置移开的时候调用这个方法。MoveMe程序仅跟踪发生在Welcome按钮上的移动。这个方法检查触摸事件的位置,并用之调整Placard对象的中点。视图的系统会引发在新位置的自动重绘。
列表6 响应触摸的移动
- (void)touchesMoved:(NSSet *)touches withEvent:(UIEvent *)event { UITouch *touch = [touches anyObject]; // If the touch was in the placardView, move the placardView // to its location if ([touch view] == placardView) { CGPoint location = [touch locationInView:self]; placardView.center = location; return; } }
当用户的手指离开屏幕,MoveMe用动画的形式把按钮移动回它的原始位置,程序窗口的中点。列表7展示了开始动画的touchesEnded:withEvent:方法。
列表7 释放Welcome按钮
- (void)touchesEnded:(NSSet *)touches withEvent:(UIEvent *)event { UITouch *touch = [touches anyObject]; // If the touch was in the placardView, bounce it back to the center if ([touch view] == placardView) { // Disable user interaction so subsequent touches // don't interfere with animation self.userInteractionEnabled = NO; [self animatePlacardViewToCenter]; return; } }
为了简化程序的事件处理流程,touchesEnded:withEvent:方法在按钮以动画方式返回原始位置的过程中暂时禁用了新的触摸事件。如果不这么做的话,每个事件处理方法中都需要判断按钮是不是正在动画中,如果是就要终止动画。在按钮返回屏幕中心的时候短时间禁用用户交互,简化了事件处理代码,无非额外的逻辑。当按钮达到他的原始位置,MoveMeView类的 animationDidStop:finished:方法重新启用用户交互,这样事件循环又可以继续下去。
更多关于在iPhone OS下处理事件的信息,参看iPhone OS编程指南的事件处理章节。
按钮动画移动
在iPhone程序中,动画起了非常重要的作用。动画广泛用于给用户相关信息以及立即响应。例如,当用户在程序中浏览层级数据,iPhone不是仅仅直接用其他的屏幕代替当前的屏幕,而是让屏幕以动画的方式就位。移动的方向用户走向层级的高层还是低层,并给用户可视化的信息这里有新的信息。
因为动画很重要,所以UIKit的类中已经内建了对它的支持。MoveMe程序获益于这种支持,动画改变Welcome按钮的外观。当用户第一次触摸按钮,程序引发一个动画让按钮的尺寸变大。
当用户放开这个按钮,另外的动画产生让按钮回到原始位置。创建这样动画的步骤如下:
- 调用你想动画的视图的beginAnimations:context:方法。
- 设置动画的属性。
- 调用commitAnimations方法开始动画。
列表8展示Welcome按钮第一次被触摸的时候令它动画的代码。这个方法设置了动画的持续时间,并对按钮实施放大操作。当动画结束,动画的机制调用委托的 growAnimationDidStop:finished:context:方法,通过令按钮轻微晃动的方式结束动画,并把placard视图移动到触点。
列表8 让Welcome产生动画
- (void)animateFirstTouchAtPoint:(CGPoint)touchPoint { #define GROW_ANIMATION_DURATION_SECONDS 0.15 NSValue *touchPointValue = [[NSValue valueWithCGPoint:touchPoint] retain]; [UIView beginAnimations:nil context:touchPointValue]; [UIView setAnimationDuration:GROW_ANIMATION_DURATION_SECONDS]; [UIView setAnimationDelegate:self]; [UIView setAnimationDidStopSelector: @selector(growAnimationDidStop:finished:context:)]; CGAffineTransform transform = CGAffineTransformMakeScale(1.2, 1.2); placardView.transform = transform; [UIView commitAnimations]; } - (void)growAnimationDidStop:(NSString *)animationID finished:(NSNumber *)finished context:(void *)context { #define MOVE_ANIMATION_DURATION_SECONDS 0.15 [UIView beginAnimations:nil context:NULL]; [UIView setAnimationDuration:MOVE_ANIMATION_DURATION_SECONDS]; placardView.transform = CGAffineTransformMakeScale(1.1, 1.1); // Move the placard view under the touch NSValue *touchPointValue = (NSValue *)context; placardView.center = [touchPointValue CGPointValue]; [touchPointValue release]; [UIView commitAnimations]; }