我有一个视图叠加在许多其他视图之上。我仅使用过分检测屏幕上的某些触摸,但除此之外,我不希望该视图停止其下的其他视图(例如滚动视图)的行为。我如何转发所有触摸此叠加视图?它是UIView的子目录。
我有一个视图叠加在许多其他视图之上。我仅使用过分检测屏幕上的某些触摸,但除此之外,我不希望该视图停止其下的其他视图(例如滚动视图)的行为。我如何转发所有触摸此叠加视图?它是UIView的子目录。
Answers:
我只需要禁用用户交互即可!
目标C:
myWebView.userInteractionEnabled = NO;
迅速:
myWebView.isUserInteractionEnabled = false
myWebView.isUserInteractionEnabled = false
要将触摸从叠加视图传递到下面的视图,请在UIView中实现以下方法:
目标C:
- (BOOL)pointInside:(CGPoint)point withEvent:(UIEvent *)event {
NSLog(@"Passing all touches to the next view (if any), in the view stack.");
return NO;
}
斯威夫特5:
override func point(inside point: CGPoint, with event: UIEvent?) -> Bool {
print("Passing all touches to the next view (if any), in the view stack.")
return false
}
这是一个旧线程,但是在搜索时出现了,因此我想添加2c。我有一个包含子视图的UIView,并且只想拦截击中一个子视图的触摸,所以我修改了PixelCloudSt的答案
-(BOOL)pointInside:(CGPoint)point withEvent:(UIEvent *)event
{
for (UIView* subview in self.subviews ) {
if ( [subview hitTest:[self convertPoint:point toView:subview] withEvent:event] != nil ) {
return YES;
}
}
return NO;
}
@fresidue答案的改进版本。您可以将此UIView
子类用作透明视图,以将触摸传递到其子视图之外。在Objective-C中的实现:
@interface PassthroughView : UIView
@end
@implementation PassthroughView
- (BOOL)pointInside:(CGPoint)point withEvent:(UIEvent *)event
{
for (UIView *view in self.subviews) {
if (!view.hidden && [view pointInside:[self convertPoint:point toView:view] withEvent:event]) {
return YES;
}
}
return NO;
}
@end
。
和在 Swift中:
class PassthroughView: UIView {
override func point(inside point: CGPoint, with event: UIEvent?) -> Bool {
return subviews.contains(where: {
!$0.isHidden
&& $0.isUserInteractionEnabled
&& $0.point(inside: self.convert(point, to: $0), with: event)
})
}
}
假设您有一个大的“ holder”面板,也许后面有一个表格视图。您创建“持有人”面板PassthroughView
。现在它将起作用,您可以在“持有人”中滚动表。
但!
在“持有人”面板的顶部,您有一些标签或图标。别忘了,当然,这些标记必须简单地标记为用户交互已启用!
在“持有人”面板的顶部,有一些按钮。别忘了,当然,这些必须简单地标记为启用用户交互功能!
注意的是有点混乱,在“持有人”本身-您使用的视图PassthroughView
上-必须标明用户交互启用ON!这是ON!(否则,将永远不会调用PassthroughView中的代码。)
$0.isUserInteractionEnabled
我有一个类似的问题与UIStackView(但可以是任何其他视图)。我的配置如下:
在经典情况下,我有一个需要放置在背景中且侧面带有按钮的容器。出于布局目的,我将按钮包括在UIStackView中,但是现在stackView的中间(空)部分截取了触摸:-(
我所做的是创建一个UIStackView的子类,该类具有定义应该可触摸的subView的属性。现在,对侧面按钮(包含在* viewsWithActiveTouch *数组中)的任何触摸都将被赋予按钮,而除这些视图之外的其他任何视图上的任何触摸都不会被拦截,因此传递给堆栈下方的任何内容视图。
/** Subclass of UIStackView that does not accept touches, except for specific subviews given in the viewsWithActiveTouch array */
class NoTouchStackView: UIStackView {
var viewsWithActiveTouch: [UIView]?
override func hitTest(point: CGPoint, withEvent event: UIEvent?) -> UIView? {
if let activeViews = viewsWithActiveTouch {
for view in activeViews {
if CGRectContainsPoint(view.frame, point) {
return view
}
}
}
return nil
}
}
如果您要将触摸转发到的视图并非恰好是子视图/超级视图,则可以在UIView子类中设置自定义属性,如下所示:
@interface SomeViewSubclass : UIView {
id forwardableTouchee;
}
@property (retain) id forwardableTouchee;
确保在您的.m中将其合成:
@synthesize forwardableTouchee;
然后在您的任何UIResponder方法中包括以下内容:
- (void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event {
[self.forwardableTouchee touchesBegan:touches withEvent:event];
}
无论您在何处实例化UIView,都将forwardableTouchee属性设置为要将事件转发到的任何视图:
SomeViewSubclass *view = [[[SomeViewSubclass alloc] initWithFrame:someRect] autorelease];
view.forwardableTouchee = someOtherView;
我需要通过UIStackView进行触摸。内部的UIView是透明的,但UIStackView消耗了所有触摸。这对我有用:
class PassThrouStackView: UIStackView {
override func hitTest(_ point: CGPoint, with event: UIEvent?) -> UIView? {
let view = super.hitTest(point, with: event)
if view == self {
return nil
}
return view
}
}
所有ArrangementSubviews仍然会收到触摸,但是UIStackView本身的触摸进入了下面的视图(对我来说是mapView)。
看起来,即使您在这里有很多答案,也没有我需要的一切。所以我从这里的@fresidue那里获取了答案,并将其转换为swift,因为现在大多数开发人员都希望在这里使用它。
它解决了我的问题,我有一些带有按钮的透明工具栏,但我希望工具栏对于用户不可见,并且应该经历触摸事件。
isUserInteractionEnabled = false,因为根据我的测试,这不是一个选项。
override func point(inside point: CGPoint, with event: UIEvent?) -> Bool {
for subview in subviews {
if subview.hitTest(convert(point, to: subview), with: event) != nil {
return true
}
}
return false
}
我在StackView中有几个标签,但是上述解决方案并没有取得太大的成功,而是使用以下代码解决了我的问题:
let item = ResponsiveLabel()
// Configure label
stackView.addArrangedSubview(item)
子类化UIStackView:
class PassThrouStackView:UIStackView{
override func hitTest(_ point: CGPoint, with event: UIEvent?) -> UIView? {
for subview in self.arrangedSubviews {
let convertedPoint = convert(point, to: subview)
let labelPoint = subview.point(inside: convertedPoint, with: event)
if (labelPoint){
return subview
}
}
return nil
}
}
然后,您可以执行以下操作:
class ResponsiveLabel:UILabel{
override func touchesBegan(_ touches: Set<UITouch>, with event: UIEvent?) {
// Respond to touch
}
}
试试这样的东西...
for (UIView *view in subviews)
[view touchesBegan:touches withEvent:event];
例如,上面的代码在您的touchesBegan方法中会将触摸传递给视图的所有子视图。
我试图做的情况是使用嵌套的UIStackView内部的控件来构建控制面板。一些控件具有UITextField的其他控件和UIButton的控件。此外,还有用于标识控件的标签。我想做的是在控制面板后面放一个大的“不可见”按钮,这样,如果用户在按钮或文本字段之外的区域上轻按,我就可以接住并采取行动-如果出现文本,则首先关闭任何键盘字段处于活动状态(resignFirstResponder)。但是,点击控制面板上的标签或其他空白区域不会使物体通过。以上讨论有助于在下面提出我的答案。
基本上,我将UIStackView子类化,并重写“ point(inside:with)例程”以查找需要触摸的控件类型,并“忽略”我想忽略的标签之类的东西。它还检查UIStackView的内部,以便可以将其递归到控制面板结构中。
该代码可能比应有的更为冗长。但这对调试很有帮助,希望可以使例程的工作更加清晰。只需确保在Interface Builder中将UIStackView的类更改为PassThruStack。
class PassThruStack: UIStackView {
override func point(inside point: CGPoint, with event: UIEvent?) -> Bool {
for view in self.subviews {
if !view.isHidden {
let isStack = view is UIStackView
let isButton = view is UIButton
let isText = view is UITextField
if isStack || isButton || isText {
let pointInside = view.point(inside: self.convert(point, to: view), with: event)
if pointInside {
return true
}
}
}
}
return false
}
}
正如@PixelCloudStv所建议的,如果您希望将触摸从一个视图扔到另一个视图,但是对此过程具有一些其他控制权,则可以使用-UIView子类
//标题
@interface TouchView : UIView
@property (assign, nonatomic) CGRect activeRect;
@end
//实施
#import "TouchView.h"
@implementation TouchView
#pragma mark - Ovverride
- (BOOL)pointInside:(CGPoint)point withEvent:(UIEvent *)event
{
BOOL moveTouch = YES;
if (CGRectContainsPoint(self.activeRect, point)) {
moveTouch = NO;
}
return moveTouch;
}
@end
在interfaceBuilder中之后,只需将View类设置为TouchView并使用rect设置active rect。您也可以更改和实现其他逻辑。