Tag: 手势识别

Gesturizer:在3D-Touch设备上为iOS应用启用手势交互

取决于上下文和任务,由手写笔或手指在移动设备上进行的手势比与按钮和菜单进行标准交互更有效和方便。 然而,手势和相关命令的记忆引起一些困难。 另外,由于有限的屏幕空间和屏幕上已经存在的可单击/可移动UI元素,将手势交互应用于应用程序的操作受到限制。 为了解决这些问题,让我介绍一下iOSGesturizer库。 简而言之,该库允许在3D触摸设备上运行的任何iOS应用上基于单笔划手势的交互,而不会干扰现有的UI元素。 有关更多详细信息,让我们仔细看一下该示例。 这是他们的教程中使用的简单的Apple的FoodTracker项目。 通过CocoaPods将iOSGesturizer导入此项目后,从该项目中打开两个文件。 在AppDelegate.swift文件中,初始化窗口:UIWindow变量作为GesturizerWindow类的实例: var window:UIWindow? = GesturizerWindow() 现在打开文件MealTableViewController.swift并添加以下内容: 覆盖func viewDidAppear(_动画:布尔){ super.viewDidAppear(动画) 让window = UIApplication.shared.keyWindow! 如! 手势窗口 让view = GesturizerView() view.gestureHandler = { 让警报= UIAlertView() alert.message = view.names [索引] alert.addButton(withTitle:“确定”) alert.show() } window.setGestureView(视图:视图) } 通过gestureHandler,我们定义了手势侦听器方法。 在这种情况下,手势名称(剪切,复制或粘贴)会在警报消息中弹出。 现在,运行项目: 如您所见,该应用程序可识别三种类型的手势。 要显示可能的手势(例如左侧),您需要用力触摸并等待一秒钟,然后用手指进行所需的动作。 学习后,您无需等待即可执行手势(右侧示例)。 从体系结构的角度来看,这整个过程看起来像这样: GesturizerWindow首先处理所有屏幕触摸,并确定是否将事件传递给GesturizerView或下面的视图。 值得一提的是,只有GesturizerView才需要在“训练模式”下显示手势,而在GesturizerWindow中则需要进行按压力和手势识别的处理。 对我们来说幸运的是,GesturizerWindow不会在应用程序中造成延迟,因为手势是使用快速简单的算法1 $ -Recognizer识别的,不需要训练数据也无需学习。 如果您认为这个想法很有趣,那么我正在等待请求。 PS我计划添加一般自定义手势和参数的功能。

iOS上的事件传递:第3部分

我们已经通过iOS中的事件传递系统结束了旅程。 在介绍目标动作模式如何工作以及如何使用某些相同的API沿响应者链发送自定义事件之前,让我们回顾一下到目前为止所涵盖的内容。 在第1部分中,我们研究了UIKit如何通过点击测试处理触摸事件以及手势识别器在系统中的位置。 我们还简要地研究了响应者链如何处理这些触摸事件,包括它们遵循的到达视图和视图控制器层次结构的路径。 在第2部分中,我们介绍了UIResponder中定义的其余事件,以及有关Responder Chain如何运行的更多信息。 目标行动 UIKit大量使用目标动作模式。 它在UIControl中定义。 让我们看一看UIControl标题的代码段。 NS_CLASS_AVAILABLE_IOS(2_0)@interface UIControl:UIView —-剪断—- -(void)addTarget:(nullable id)目标动作:(SEL)ControlEvents的动作:(UIControlEvents)controlEvents; -(void)removeTarget:(nullable id)目标动作:(nullable SEL)针对ControlEvents:(UIControlEvents)controlEvents的动作; -(NSSet *)allTargets; -(UIControlEvents)allControlEvents; -(nullable NSArray *)actionsForTarget:(nullable id)target for ControlEvent:(UIControlEvents)controlEvent; -(void)sendAction:(SEL)action to:(nullable id)target forEvent:(nullable UIEvent *)event; -(void)sendActionsForControlEvents:(UIControlEvents)controlEvents; —-剪断—- 这些方法定义了目标动作模式。 该模式使我们可以定义要处理的事件,接收事件的目标以及要发送到目标的动作(消息)。 事件和动作的这种分离还使我们可以为一个或多个动作定义多个收件人。 有关UIControl和目标动作模式的更多信息,请查看Apple的UIControl文档。 那么,这与活动交付相适应吗? 当前, UIControl使用带有类似于以下内容的调用跟踪来发送操作: 框架#0:-[BPXLTableViewCell cellButtonTapped:] 框架#1:-[UIApplication sendAction:to:from:forEvent:] 框架2:-[UIControl sendAction:to:forEvent:] 框架#3:-[UIControl _sendActionsForEvents:withEvent:] 框架4:-[UIControl touchesEnded:withEvent:] 框架5:_UIGestureEnvironmentSortAndSendDelayedTouches —剪- 这里重要的API调用是-[UIApplication […]

iOS上的事件传递:第1部分

如果您的iOS应用程序可以处理轻击,滑动,平移或任何其他外部交互,则它使用的是幕后事件。 这些事件采用的路径是一个定义明确的过程,我们将研究它的工作方式。 当调试一些棘手的问题(涉及文本输入或远程控制事件)时,了解此过程的工作原理很方便。 您甚至可以使用此知识使自定义数据流通过您的应用程序。 这是有关事件传递的系列文章中的第一篇。 它将涵盖触摸处理,如何将简单的触摸转变为通过应用程序传递的事件以及如何为组件提供处理该事件的机会。 触摸处理 触摸事件是iOS应用程序处理的事件的主要形式。 我们每天使用的API隐藏了许多如何处理触摸的细节。 了解如何在应用程序中传递这些事件可以帮助确定应用程序的构建方式。 您甚至可以使用此基础结构传递自定义事件。 命中测试 在讨论事件传递时,要讨论的第一件事是系统如何处理触摸事件以及这些事件通过应用程序的路径。 一旦用户点击屏幕,事件就会开始。 在适当的组件可以处理触摸之前,系统需要确定在何处发生触摸以及谁在响应该触摸时首先遇到裂缝。 这是命中测试起作用的地方。 命中测试过程中涉及的方法是hitTest:withEvent:和pointInside:withEvent:。 hitTest:withEvent:使用pointInside:withEvent:来确定要命中的测试点是否在其范围内。 如果不在边界内,则返回nil并跳过视图层次结构的整个分支。 如果测试点在范围内,则在每个子视图上调用pointInside:withEvent:。 对于从pointInside:withEvent:返回YES的子视图,将调用hitTest:withEvent:。 原始hitTest:withEvent:调用的最终结果是子视图之一或self的结果(如果其所有子视图均返回nil)。 采取以下视图层次结构: 假设用户在View E中轻按。该过程从View A中的hitTest:withEvent:开始 。 pointInside:withEvent:对于视图A返回YES ,因此在视图B和视图C上调用pointInside:withEvent:。视图B返回NO 。 视图C返回YES,因此将其调用hitTest:withEvent:。 视图C与视图D和视图E遵循相同的过程。视图D在pointInside:withEvent:上返回NO 。 视图E返回YES ,然后在hitTest:withEvent:上返回自身,因为它没有任何子视图。 假设视图D是视图C的子视图,那么如果用户在视图D超出视图C的范围内点击,会发生什么? (当clipsToBounds为NO时,可能会发生这种情况。)视图A启动上述过程。 视图B和视图C的pointInside:withEvent:均返回NO ,因此视图A最终获得了触摸。 现在,我们有了hitTest:withEvent:的视图。 该视图称为“命中测试”视图。 现在,它与触摸相关联,在触摸处于活动状态时,将获得第一个机会以响应任何触摸事件(稍后会详细介绍)。 如果没有针对给定触摸的自定义实现,会发生什么? 这取决于。 如果视图是由视图控制器管理的,则视图控制器将有机会做出响应。 如果该视图控制器没有响应,则为命中测试视图的超级视图提供响应的机会。 一直重复此过程,直到称为“响应者链”为止。 响应者链 响应者链是责任链设计模式的一种实现。 响应者链中的每个参与者都继承自UIResponder。 UIResponder包含处理各种类型事件的离散方法。 除了触摸事件外,UIResponder还声明用于处理输入视图,运动事件,按下事件和远程控制事件的方法。 对于许多此类事件, firstResponder很重要。 firstResponder是第一个获得处理事件机会的对象。 […]

在iOS视图上点击检测,无需重复。

这篇文章介绍了一种处理UIView实例上的轻击检测的方法。 它显示了如何在新实体中提取重复行为并将其与UIView的实例组成,而不是扩展UIView类,如何提高简单性,可重用性和可扩展性。 如今的问题 用户点击UIView时触发动作就像将UITapGestureRecognizer添加到视图并使用目标/动作机制执行动作一样简单。 如果我们想检测更多UIView的点击,我们会发现自己在重复: 手势的实例化 在视图中添加手势 选择器方法的实现,以及 方法实现内部的手势状态检查。 但是唯一不同的是: 哪个视图变得可以轻敲,以及 点击事件发生时执行的操作。 因此,在一个更好的世界中,做这样的事情就足够了: 或这个: 我们怎样才能做到这一点? 拟议的解决方案 我们将定义一个新的Tap类型,该类型将Tap-on-Action行为添加到视图:此类型的对象接收视图以及在Tap事件发生并连接它们时执行的动作。 基本的Tap类是: 手势动作的目标是这个新的Tap对象,因此另一个对象需要对其进行强有力的引用。 否则,它将被释放,手势事件发生时目标将为nil ,并且将不执行任何操作。 解决此问题的第一种方法是将Tap对象保留在视图上下文中(通常是UIViewController)。 这种方法不切实际,因为我们将需要为每个使我们可以Tap的视图保持每个Tap的引用,并且我们仍将重复没有意义的样板代码。 您可以在这个操场上看到这种方法。 保持引用与我们的点击操作无关,这是一个技术细节,一种更好的方法是让Tap类型处理引用保持,以保持界面整洁和易于使用。 怎么样? 使用创建Tap实例并将其保存在集合中的类方法。 像这样: 您可以在这个操场上看看这种方法。 Tap类将保留其实例,但应在释放其视图时释放它们的每个实例。 我们如何检测到这一点? 好吧,我认为一种非常简单的方法正在Tap和他的视图之间的引用,因此稍后,当我们找到带有nil视图的Tap对象时,可以释放它。 我们可以定期或每次创建新的Tap时触发此“清理”操作。 最终解决方案的完整代码可在此处获得,我们可以像这样使用它: 其他解决方案 使用继承:我们可以创建一个继承自UIView的TappableView类型。 该子类添加了onTapDo:方法。 添加手势target-action时,我们还将有一个私有方法用作选择器。 以这种方式进行操作,我们不是使UIView轻敲,而是创建一种新型的可轻敲的UIView :任何想要轻敲的视图都应继承自TappableView而不是原始UIView 。 如果我们要检测UILabel ( UIView子类)上的轻UIView ,则不能。 另外,我们使用继承来避免复制,这不是正确使用继承。 您可以在这个操场上看看这种方法。 使用协议扩展:我们可以定义一个协议可Tappable的协议,并使用UIView的默认实现的协议扩展,但是由于协议扩展不支持存储的属性,因此我们无法存储执行轻Tappable时要执行的块。 无论如何,一种解决方法是使用关联的对象在运行时向该对象添加新变量。 这种方法的缺点是,除了要用obj-c运行时接触UIView对象的内脏之外,当我们要使任何 UIView都可以被轻敲时,我们要使所有 UIView轻敲,而不是全部 。 我们可以扩展自定义视图类型的协议,而不是普通的旧UIView ,但是我们再次创建新的类型,而不是使任何UIView都可轻敲。 […]

iOS中的自定义强制触摸手势

好的,然后-强制触摸。 首先,我不知道如何以及何时处理此问题。 最初,我考虑过使用默认的方式来处理“强制触摸”,但是这种情况在某种程度上是特定的,因此我进入了UIGestureRecognizer的自定义实现。 这里的最终目标是能够强制触摸特定的UICollectionViewCell并具有用于背景调光的同时动画以及从触摸点扩展的两个动作图标的功能,因此开发了一种自定义方法。 协议 因此,一切都始于协议,该协议在手势动作和主控制器本身之间来回移动。 首先接收与移动开发相关的新故事。 —点击该跟随按钮 推特:@ ChiliLabs www.chi.lv