为什么 UINavigationBar 会窃取触摸事件?
我有一个自定义 UIButton,其中 UILabel 添加为子视图。仅当我触摸顶部边界下方约 15 个点时,按钮才会执行给定的选择器。当我点击该区域上方时,什么也没有发生。
我发现这并不是由错误创建按钮和标签引起的,因为在我将按钮向下移动约 15 px 后,它可以正常工作。
更新 我忘了说位于 UINavigationBar 下方的按钮和按钮上部的 1/3 不会收到触摸事件。
带有 4 个按钮的视图是位于导航栏下方。当触摸顶部的“篮球”时,后退按钮获得触摸事件,当触摸顶部的“钢琴”时,右栏按钮(如果存在)获得触摸。如果不存在,则什么也没有发生。
我在应用程序文档中没有找到此记录功能。
我还发现这个主题与我的相关问题,但也没有答案。
I have a custom UIButton with UILabel added as subview. Button perform given selector only when I touch it about 15points lower of top bound. And when I tap above that area nothing happens.
I found out that it hasn't caused by wrong creation of button and label, because after I shift the button lower at about 15 px it works correctly.
UPDATE I forgot to say that button located under the UINavigationBar and 1/3 of upper part of the button don't get touch events.
View with 4 buttons is located under the NavigationBar. And when touch the "Basketball" in top, BackButton get touch event, and when touch "Piano" in top, then rightBarButton (if exists) get touch. If not exists, nothing happened.
I didn't find this documented feature in App docs.
Also I found this topic related to my problem, but there is no answer too.
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(12)
我注意到,如果您将 userInteractionEnabled 设置为 OFF,导航栏将不再“窃取”触摸。
因此,您必须对 UINavigationBar 进行子类化,并在 CustomNavigationBar 中执行以下操作:
有关如何对 UINavigationBar 进行子类化的信息,您可以找到 此处。
I noticed that if you set userInteractionEnabled to OFF, the NavigationBar doesn't "steal" the touches anymore.
So you have to subclass your UINavigationBar and in your CustomNavigationBar do this:
Info about how to subclass UINavigationBar you can find here.
我在这里(Apple开发者论坛)找到了答案。
Keith 在 Apple 开发者技术支持上,2010 年 5 月 18 日(iPhone OS 3):
我还发现,当我触摸 UINavigationBar 下方的区域时,location.y 定义为 64,尽管事实并非如此。
所以我做了这个:
CustomWindow.h
CustomWindow.m
在 AppDelegate 类中,我使用 CustomWindow 而不是 UIWindow。
现在,当我触摸导航栏下方的区域时,没有任何反应。
我的按钮仍然没有收到触摸事件,因为我不知道如何使用按钮将此事件(并更改坐标)发送到我的视图。
I found out the answer here(Apple Developer Forum).
Keith at Apple Developer Technical Support, on 18th May 2010 (iPhone OS 3):
Also I found out,that when I touch the area under the UINavigationBar, the location.y defined as 64,though it was not.
So I made this:
CustomWindow.h
CustomWindow.m
In AppDelegate class I use CustomWindow instead of UIWindow.
Now when I touch area under navigation bar, nothing happens.
My buttons still don't get touch events,because I don't know how to send this event (and change coordinates) to my view with buttons.
子类 UINavigationBar 并添加此方法。除非点击子视图(例如按钮),否则它将导致点击被传递。
Subclass UINavigationBar and add this method. It will cause taps to be passed through unless they are tapping a subview (such as a button).
我的解决方案如下:
第一:
在您的应用程序中添加
UINavigationBar
的扩展(无论您在何处输入此代码),如下所示:以下代码仅在点击
navigationBar
时调度带有点和事件的通知。然后,在您的特定视图控制器中,您必须通过在
viewDidLoad
中添加以下行来监听此通知:然后您需要在视图控制器中创建方法
tapNavigationBar
,如下所示:PD :不要忘记删除 deinit 中的观察,如下所示:
就是这样...这有点“棘手”,但这是一个很好的解决方法,可以避免子类化并在
navigationBar
中随时获取通知正在被窃听。The solution for me was the following one:
First:
Add in your application (It doesn't matter where you enter this code) an extension for
UINavigationBar
like so:The following code just dispatch a notification with the point and event when the
navigationBar
is being tapped.Then in your specific view controller you must listen to this notification by adding this line in your
viewDidLoad
:Then you need to create the method
tapNavigationBar
in your view controller as so:PD: Don't forget to remove the observation in the deinit like so:
That's it... That's a little bit 'tricky', but it's a good workaround for not subclassing and getting a notification anytime the
navigationBar
is being tapped.我只是想分享解决这个问题的另一个前景。这不是设计上的问题,而是为了帮助用户返回或导航。但我们需要将内容紧紧地放在导航栏内或下方,而事情看起来很糟糕。
首先我们看一下代码。
您
可能会明白为什么我要进行第二次触摸处理。有解决方案的秘诀。
一次调用会调用两次命中测试。第一次报告窗口上的实际点。一切顺利。在第二遍时,就会发生这种情况。
如果系统看到导航栏并且 Y 侧的点击点大约多出 9 像素,它会尝试将其逐渐减少到导航栏所在的 44 点以下。
看一下屏幕就清楚了。
因此,有一种机制将使用附近的逻辑来进行命中测试的第二遍。如果我们可以知道它的第二遍,然后用第一个命中测试点调用超级。工作完成了。
上面的代码正是这样做的。
I just wanted to share another prospective to solving this problem. This is not a problem by design, but it was meant to help user get back or navigate. But we need to put things tightly in or below nav bar and things look sad.
First lets look at the code.
}
You might be see why am i doing second touch handling. There is the recipe to the solution.
Hit test is called twice for a call. The first time the actual point on the window is reported. Everything goes well. On the second pass, this happens.
If system sees a nav bar and the hit point is around 9 pixels more on Y side, it tries to decrease that gradually to below 44 points which is where the nav bar is.
Take a look at the screen to be clear.
So theres a mechanism that will use nearby logic to the second pass of hittest. If we can know its second pass and then call the super with first hit test point. Job done.
The above code does that exactly.
有两件事可能会导致问题。
您是否尝试过
setUserInteractionEnabled:NO
标签。我认为可能有效的第二件事是在按钮顶部添加标签后,您可以将标签发送到后面(它可能有效,但不确定)
[button sendSubviewToBack:label];
请让我知道代码是否有效:)
There are 2 things that might be causing problems.
Did you try
setUserInteractionEnabled:NO
for the label.Second thing i think might work is apart from that after adding label on top of button you can send the label to back (it might work, not sure although)
[button sendSubviewToBack:label];
Please let me know if the code works :)
你的标签很大。它们从
{0,0}
(按钮的左上角)开始,延伸到按钮的整个宽度,并具有整个视图的高度。检查您的帧
数据并重试。此外,您还可以选择使用
UIButton
属性titleLabel
。也许您稍后设置标题,并将其放入此标签而不是您自己的UILabel
中。这可以解释为什么文本(属于按钮)会起作用,而标签会覆盖按钮的其余部分(不让点击通过)。titleLabel
是一个只读属性,但您可以将其自定义为您自己的标签(frame
除外),包括文本颜色、字体、阴影等。Your labels are huge. They start at
{0,0}
(the left top corner of the button), extend over the entire width of the button and have a height of the entire view. Check yourframe
data and try again.Also, you have the option of using the
UIButton
propertytitleLabel
. Maybe you are setting the title later and it goes into this label rather than your ownUILabel
. That would explain why the text (belonging to the button) would work, while the label would be covering the rest of the button (not letting the taps go through).titleLabel
is a read-only property, but you can customize it just as your own label (except perhaps theframe
) including text color, font, shadow, etc.这解决了我的问题..
我添加了 hitTest:withEvent :代码到我的导航栏子类..
This solved my problem..
I added hitTest:withEvent: code to my navbar subclass..
扩展 Alexander 的解决方案:
步骤 1. 子类
UIWindow
步骤 2:将
ChunyuWindow
实例分配给AppDelegate
实例步骤 3:实现
touchesEnded:widthEvent:
对于带有按钮的view
,例如:第四步:当我们的view我们调用
ChunyuWindow
的addViewForTouchPriority
关心出现,视图消失或dealloc时调用removeViewForTouchPriority
,在ViewControllers的viewDidAppear/viewDidDisappear/dealloc中,所以ChunyuWindow
中的_touchView为NULL,和UIWindow
,没有副作用。Extending Alexander's solution:
Step 1. Subclass
UIWindow
Step 2: Assign
ChunyuWindow
instance toAppDelegate
InstanceStep 3: Implement
touchesEnded:widthEvent:
forview
with buttons, for example:Step 4: call
ChunyuWindow
'saddViewForTouchPriority
when the view we care about appears, and callremoveViewForTouchPriority
when the view disappears or dealloc, in viewDidAppear/viewDidDisappear/dealloc of ViewControllers, so _touchView inChunyuWindow
is NULL, and it is the same asUIWindow
, having no side effects.根据 Alexander 提供的答案,对我有用的替代解决方案:
您可以禁用负责 UINavigationBar< 上“倾斜区域”的手势识别器,而不是覆盖
UIWindow
/代码>。An alternative solution that worked for me, based on the answer provided by Alexandar:
Instead of overriding the
UIWindow
, you can just disable the gesture recogniser responsible for the "slop area" on theUINavigationBar
.根据巴特·怀特利(Bart Whiteley)的说法,给出一个扩展版本。不需要子类化。
Give a extension version according to Bart Whiteley. No need to subclass.
以下对我有用:
The following worked for me: