57、iOS触摸事件与手势处理全解析

iOS触摸事件与手势处理全解析

在iOS开发中,触摸事件和手势处理是非常重要的一部分,它直接影响着用户与应用程序的交互体验。下面我们将详细探讨iOS中触摸事件的转发、多点触摸架构、触摸通知方法,以及通过两个具体的应用实例来加深理解。

事件转发:保持响应链的活跃

在iOS应用中,响应链是处理触摸事件的关键机制。以邮件应用中的表格视图单元格为例,假设该单元格只处理删除滑动手势。当触摸事件发生时,单元格需要判断该事件是否为其能处理的滑动手势。
- 事件匹配 :如果事件匹配单元格所期望的滑动手势,单元格会采取相应动作,事件处理结束。
- 事件不匹配 :若事件不匹配,单元格有责任将该事件手动转发给响应链中的下一个对象。否则,表格及响应链上的其他对象将无法响应,应用可能无法按用户预期运行。

以下是一个虚构的事件响应方法示例,展示了如何将事件转发给下一个响应者:

- (void)respondToFictionalEvent:(UIEvent *)event {
    if ([self shouldHandleEvent:event]) {
        [self handleEvent:event];
    } else {
        [[self nextResponder] respondToFictionalEvent:event];
    }
}

在实际开发中,当对象拦截到自己无法处理的事件时,应手动将其传递给下一个响应者,以确保响应链的正常工作。

多点触摸架构

手势通过事件嵌入在响应链中传递,因此处理多点触摸屏幕交互的代码需要包含在响应链中的对象里。通常,我们可以选择将代码嵌入 UIView 的子类或 UIViewController 中。
- 代码嵌入视图 :如果视图需要根据用户触摸对自身进行操作,代码可能应放在定义该视图的类中。例如, UISwitch UISlider 等控件类会响应触摸相关事件,它们的手势处理代码就嵌入在类中。
- 代码嵌入视图控制器 :当处理的手势影响的不仅仅是被触摸的对象时,手势代码应放在相关的视图控制器类中。例如,用户触摸一行并指示删除所有行的手势,应由视图控制器中的代码处理。

四个触摸通知方法

iOS系统提供了四个方法来通知响应者关于触摸事件:
1. touchesBegan:withEvent: :当用户首次触摸屏幕时,系统会查找实现了该方法的响应者。通过该方法,我们可以获取触摸的点击次数和触摸数量。

- (void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event  {
    NSUInteger numTaps = [[touches anyObject] tapCount];
    NSUInteger numTouches = [event.allTouches count];
    // Do something here.
}
  1. touchesMoved:withEvent: :当用户手指在屏幕上移动时,该方法会被多次调用。我们可以通过它获取每个手指的当前位置和之前的位置。
  2. touchesEnded:withEvent: :当用户的任何手指离开屏幕时,调用该方法,表示手势结束。
  3. touchesCancelled:withEvent: :如果用户在手势进行中被其他事件中断(如来电),该方法会被调用,可在此进行清理工作。

在这些方法中, allTouches 属性包含当前所有按压在屏幕上的手指对应的 UITouch 对象,而 touches 参数包含刚刚添加、移除、移动或停止移动的手指对应的 UITouch 对象。

TouchExplorer应用实践

为了更好地理解四个触摸通知方法的调用时机,我们可以创建一个名为 TouchExplorer 的应用。
操作步骤如下
1. 在Xcode中,使用单视图应用模板创建新项目,产品名称为 TouchExplorer ,设备选择 Universal
2. 在 ViewController.m 的类扩展中添加三个输出口:

#import "ViewController.h"

@interface ViewController ()
@property (weak, nonatomic) IBOutlet UILabel *messageLabel;
@property (weak, nonatomic) IBOutlet UILabel *tapsLabel;
@property (weak, nonatomic) IBOutlet UILabel *touchesLabel;
@end
  1. 编辑 Main.storyboard
    • 拖动一个标签到视图的左上角,按住 Option 键复制两个标签,使它们垂直排列。
    • 设置标签的自动布局约束:在文档大纲中,从第一个标签 Control 拖动到主视图,按住 Shift 键选择“Top Space to Top Layout Guide”和“Leading Space to Container Margin”,对其他两个标签执行相同操作。
    • 将标签连接到对应的输出口:从视图控制器图标 Control 拖动到三个标签,分别连接到 messageLabel tapsLabel touchesLabel
    • 双击每个标签并按 Delete 键清除文本。
  2. 确保视图的用户交互和多点触摸功能启用:单击视图背景或文档大纲中的视图图标,打开属性检查器,在视图部分勾选“User Interaction Enabled”和“Multiple Touch”。
  3. ViewController.m @implementation 部分添加以下代码:
@implementation ViewController

- (void)viewDidLoad
{
    [super viewDidLoad];
    // Do any additional setup after loading the view, typically from a nib.
}

- (void)didReceiveMemoryWarning
{
    [super didReceiveMemoryWarning];
    // Dispose of any resources that can be recreated.
}

- (void)updateLabelsFromTouches:(NSSet *)touches {
    NSUInteger numTaps = [[touches anyObject] tapCount];
    NSString *tapsMessage = [[NSString alloc]
                             initWithFormat:@"%ld taps detected", (unsigned long)numTaps];
    self.tapsLabel.text = tapsMessage;

    NSUInteger numTouches = [touches count];
    NSString *touchMsg = [[NSString alloc] initWithFormat:
                          @"%ld touches detected", (unsigned long)numTouches];
    self.touchesLabel.text = touchMsg;
}

#pragma mark - Touch Event Methods
- (void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event {
    self.messageLabel.text = @"Touches Began";
    [self updateLabelsFromTouches:event.allTouches];
}

- (void)touchesCancelled:(NSSet *)touches withEvent:(UIEvent *)event {
    self.messageLabel.text = @"Touches Cancelled";
    [self updateLabelsFromTouches: event.allTouches];
}

- (void)touchesEnded:(NSSet *)touches withEvent:(UIEvent *)event {
    self.messageLabel.text = @"Touches Ended.";
    [self updateLabelsFromTouches: event.allTouches];
}

- (void)touchesMoved:(NSSet *)touches withEvent:(UIEvent *)event {
    self.messageLabel.text = @"Drag Detected";
    [self updateLabelsFromTouches: event.allTouches];
}

@end

编译并运行应用,在模拟器中,我们可以通过多次点击屏幕增加点击次数,点击并按住鼠标拖动来模拟触摸和拖动。同时,按住 Option 键并点击鼠标拖动可以模拟双指捏合,按住 Option Shift 键可以模拟双指滑动。如果在真机上运行,我们可以尝试不同数量的手指触摸和点击,以更好地理解四个触摸方法的工作原理。

Swipes应用:检测滑动手势

接下来,我们将创建一个名为 Swipes 的应用,用于检测水平和垂直滑动手势。
操作步骤如下
1. 在Xcode中,使用单视图应用模板创建新项目,设备选择 Universal ,项目名称为 Swipes
2. 在 ViewController.m 的类扩展中添加以下代码:

#import "ViewController.h"

@interface ViewController ()
@property (weak, nonatomic) IBOutlet UILabel *label;
@property (nonatomic) CGPoint gestureStartPoint;
@end
  1. 编辑 Main.storyboard
    • 确保视图控制器的视图启用了用户交互和多点触摸功能。
    • 从库中拖动一个标签到视图的上部,设置文本对齐方式为居中,并调整其他文本属性以方便阅读。
    • 在文档大纲中,从标签 Control 拖动到视图,按住 Shift 键选择“Top Space to Top Layout Guide”和“Center Horizontally in Container”。
    • 从视图控制器图标 Control 拖动到标签,连接到 label 输出口。
    • 双击标签并删除其文本。
  2. ViewController.m 中添加以下代码:
static CGFloat const kMinimumGestureLength = 25;
static CGFloat const kMaximumVariance      =  5;

@implementation ViewController

- (void)viewDidLoad
{
    [super viewDidLoad];
    // Do any additional setup after loading the view, typically from a nib.
}

- (void)didReceiveMemoryWarning
{
    [super didReceiveMemoryWarning];
    // Dispose of any resources that can be recreated.
}

#pragma mark - Touch Handling

- (void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event {
    UITouch *touch = [touches anyObject];
    self.gestureStartPoint = [touch locationInView:self.view];
}

- (void)touchesMoved:(NSSet *)touches withEvent:(UIEvent *)event {
    UITouch *touch = [touches anyObject];
    CGPoint currentPosition = [touch locationInView:self.view];

    CGFloat deltaX = fabsf(self.gestureStartPoint.x - currentPosition.x);
    CGFloat deltaY = fabsf(self.gestureStartPoint.y - currentPosition.y);

    if (deltaX >=kMinimumGestureLength && deltaY <= kMaximumVariance) {
        self.label.text = @"Horizontal swipe detected";
        dispatch_after(dispatch_time(DISPATCH_TIME_NOW, 2 * NSEC_PER_SEC),
                       dispatch_get_main_queue(),
                       ^{ self.label.text = @""; });
    } else if (deltaY >=kMinimumGestureLength &&
               deltaX <= kMaximumVariance){
        self.label.text = @"Vertical swipe detected";
        dispatch_after(dispatch_time(DISPATCH_TIME_NOW, 2 * NSEC_PER_SEC),
                       dispatch_get_main_queue(),
                       ^{ self.label.text = @""; });
    }
}

@end

touchesBegan:withEvent: 方法中,我们记录用户首次触摸的位置。在 touchesMoved:withEvent: 方法中,计算手指在水平和垂直方向上的移动距离,判断是否构成滑动手势。如果是,设置标签文本显示检测到的滑动类型,并在2秒后清除文本。

通过以上两个应用实例,我们可以更深入地理解iOS中触摸事件和手势处理的机制,从而在实际开发中更好地实现用户交互功能。

通过以上步骤,我们可以创建一个简单的iOS应用,用于检测水平和垂直滑动手势。在实际开发中,我们可以根据需求调整最小手势长度和最大偏差值,以实现更精确的手势检测。

深入剖析与总结

触摸事件与手势处理的技术要点回顾

为了更清晰地理解iOS中触摸事件和手势处理的关键技术点,我们对前面的内容进行总结和深入剖析。

技术要点 描述
事件转发 当对象无法处理触摸事件时,需要手动将其转发给响应链中的下一个对象,以确保事件能被正确处理。示例代码:
objc<br>- (void)respondToFictionalEvent:(UIEvent *)event {<br> if ([self shouldHandleEvent:event]) {<br> [self handleEvent:event];<br> } else {<br> [[self nextResponder] respondToFictionalEvent:event];<br> }<br>}<br>
多点触摸架构 处理多点触摸交互的代码可嵌入 UIView 子类或 UIViewController 中。视图自身操作相关代码放视图类,影响多个对象的手势代码放视图控制器类。
触摸通知方法 包括 touchesBegan:withEvent: touchesMoved:withEvent: touchesEnded:withEvent: touchesCancelled:withEvent: 。通过这些方法可以获取触摸的点击次数、触摸数量、手指位置等信息。
手势检测 Swipes 应用为例,通过记录触摸起始点,在手指移动时计算水平和垂直方向的移动距离,判断是否构成滑动手势。
代码逻辑流程图

下面是 Swipes 应用中滑动手势检测的逻辑流程图:

graph TD;
    A[用户触摸屏幕] --> B[touchesBegan:withEvent:记录起始点];
    B --> C[手指移动];
    C --> D[touchesMoved:withEvent:计算移动距离];
    D --> E{是否构成滑动手势};
    E -- 是 --> F[显示滑动类型标签文本];
    F --> G[2秒后清除标签文本];
    E -- 否 --> C;
实际开发中的注意事项
  • 响应链的维护 :在开发过程中,要时刻注意响应链的完整性。如果某个对象错误地拦截了事件而不进行转发,可能会导致应用的部分功能无法正常使用。例如,在自定义视图时,要确保在不需要处理某些事件时,将其正确地传递给下一个响应者。
  • 手势检测的精度 :在 Swipes 应用中,我们定义了最小手势长度和最大偏差值。在实际开发中,需要根据应用的具体需求进行调整。如果最小手势长度设置得太小,可能会误判一些小的移动为滑动手势;如果设置得太大,用户可能需要滑动很长距离才能触发手势。
  • 真机测试的重要性 :虽然模拟器可以模拟一些基本的触摸操作,但要体验完整的多点触摸功能,必须在真机上进行测试。真机的触摸响应更加真实,能够发现一些在模拟器中无法发现的问题。
拓展应用场景
  • 游戏开发 :在游戏中,触摸事件和手势处理是实现玩家交互的关键。例如,通过检测滑动手势可以控制角色的移动方向,通过点击手势可以触发角色的攻击动作。
  • 绘图应用 :绘图应用需要精确地处理用户的触摸事件,以实现画笔的移动、颜色的选择等功能。可以利用 touchesMoved:withEvent: 方法实时获取手指的位置,绘制线条和图形。
  • 导航应用 :在导航应用中,用户可能会通过双指捏合来缩放地图,通过滑动手势来移动地图。通过实现相应的手势检测代码,可以为用户提供更加便捷的操作体验。
总结

iOS中的触摸事件和手势处理是实现用户交互的重要组成部分。通过理解事件转发、多点触摸架构、触摸通知方法和手势检测的原理,并结合实际应用的开发,我们可以为用户提供更加流畅、便捷的交互体验。在实际开发中,要注意响应链的维护、手势检测的精度和真机测试的重要性,同时可以根据不同的应用场景进行拓展和创新。希望本文能够帮助开发者更好地掌握iOS触摸事件和手势处理的技术,开发出更加优秀的应用程序。

(Kriging_NSGA2)克里金模型结合多目标遗传算法求最优因变量及对应的最佳自变量组合研究(Matlab代码实现)内容概要:本文介绍了克里金模型(Kriging)多目标遗传算法NSGA-II相结合的方法,用于求解最优因变量及其对应的最佳自变量组合,并提供了完整的Matlab代码实现。该方法首先利用克里金模型构建高精度的代理模型,逼近复杂的非线性系统响应,减少计算成本;随后结合NSGA-II算法进行多目标优化,搜索帕累托前沿解集,从而获得多个最优折衷方案。文中详细阐述了代理模型构建、算法集成流程及参数设置,适用于工程设计、参数反演等复杂优化问题。此外,文档还展示了该方法在SCI一区论文中的复现应用,体现了其科学性实用性。; 适合人群:具备一定Matlab编程基础,熟悉优化算法和数值建模的研究生、科研人员及工程技术人员,尤其适合从事仿真优化、实验设计、代理模型研究的相关领域工作者。; 使用场景及目标:①解决高计算成本的多目标优化问题,通过代理模型降低仿真次数;②在无法解析求导或函数高度非线性的情况下寻找最优变量组合;③复现SCI高水平论文中的优化方法,提升科研可信度效率;④应用于工程设计、能源系统调度、智能制造等需参数优化的实际场景。; 阅读建议:建议读者结合提供的Matlab代码逐段理解算法实现过程,重点关注克里金模型的构建步骤NSGA-II的集成方式,建议自行调整测试函数或实际案例验证算法性能,并配合YALMIP等工具包扩展优化求解能力。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值