可以将文章内容翻译成中文,广告屏蔽插件可能会导致该功能失效(如失效,请关闭广告屏蔽插件后再试):
问题:
I have an app with many different buttons arranged in a calculator like, square / rectangular format. It is actually extremely similar to the default iOS calculator. There are approximately 6 rows with 4 columns each of buttons.
Problem
The problem I am having involves the buttons in the bottom row (approximately the bottom 10th of the screen on an iPhone 4). They do not behave normally when pressed in the sense that when pressed, they have to be pressed and held (for roughly just under a second) to register a "button press". This is opposed to the standard short tap.
No other buttons besides this bottom row behave in this fashion.
Additionally, if these buttons are tapped on their upper edge, they behave normally, responding as soon as they are touched. This leads me to believe that the buttons themselves are not the problems but there is some problem with the layout of my views.
It should be also noted that this problem is only present on physical devices. On the simulator, the buttons behave normally.
Context
The view containing these buttons is not the root view controller of the app. Instead it is transitioned to as so (nothing fancy here):
[self presentViewController:navController animated:YES completion:nil];
Where self is the root view controller
The view controller I am having problems with is contained within a navigation controller and is presented modally by the root view controller which you can see above.
What I have tried so far
Turning auto layout on and off: same problem
Rearranging hierarchy of views: I moved the problematic buttons on top of and behind all other
views with the same result: same problem
Multiple devices (iPhone 4, 4s, 5): same problem (although buttons respond normally on both 3.5 inch and 4 inch simulators)
Testing other apps (when buttons in this region are pressed on other apps, they behave normally)
Additional Information
- Everything is laid out in Interface Builder for the problematic view controller
- All of the buttons are system buttons with standard settings and are all exactly the same besides their text.
- All of the elements of the screen (buttons, labels, etc. ) are subviews of the "view"
- The buttons are flush against each other and should not overlap more than one or two pixels.
- The problematic buttons have dimensions: 80 width X 44 height.
- The problematic buttons are flush against the bottom of the screen
- In addition to the buttons, there is one UIImage and several labels however these are at the top of the screen and do not overlap with any of the buttons in any way.
回答1:
This sounds like an interaction between the buttons and the UIScreenEdgePanGestureRecognizer (or whatever it is) that is responsible for detecting that the user wants to bring up the system's Control Center.
There are actually two potential issues here:
There can be an interaction (i.e. conflict) between the possibility of a gesture directed at your app and a gesture directed at the system. If you have gesture recognizers, you might have to use delegate methods to mediate between them and the system's gesture recognizers.
There is a well-established bug where a tap near the screen edge (i.e. in the screen edge gesture recognizer's "zone") works but it causes the button to misbehave physically, i.e. it doesn't look as if it's been tapped even though logging shows that it has (see my answer here: https://stackoverflow.com/a/22000692/341994).
回答2:
The cause for this issue is that Apple seems to place a GestureRecognizer at the bottom of the screen that delays touches in any other view.
After fiddling around with gesture recognizers on the App's windows I came up with a solution that incorporates a subclass of UIButton:
- (BOOL)pointInside:(CGPoint)point withEvent:(UIEvent *)event
{
BOOL inside = [super pointInside: point withEvent: event];
if (inside && !self.isHighlighted && event.type == UIEventTypeTouches)
{
self.highlighted = YES;
}
return inside;
}
The given method is getting called although touchesBegan: is called delayed. A check if the view is at the bottom of the screen may be suitable to prevent any side effects that may occur with this fix.
回答3:
answer of Lukas in swift and deselect highlighted
extension UIButton {
public override func pointInside(point: CGPoint, withEvent event: UIEvent?) -> Bool {
var inside = super.pointInside(point, withEvent: event)
if inside != highlighted && event?.type == .Touches {
highlighted = inside
}
return inside
}
}
回答4:
I've written a full solution in swift based on Luka's answer. Just make any conflicting buttons conform to this class and the problem will be gone:
class BorderBugFixButton : UIButton {
override func awakeFromNib() {
super.awakeFromNib()
NSNotificationCenter.defaultCenter().addObserver(self, selector: "unHighlight", name: UIApplicationWillResignActiveNotification, object: nil)
}
deinit {
NSNotificationCenter.defaultCenter().removeObserver(self)
}
override func pointInside(point: CGPoint, withEvent event: UIEvent?) -> Bool {
let inside = super.pointInside(point, withEvent: event)
if inside != highlighted && event?.type == .Touches {
highlighted = inside
}
return inside
}
internal func unHighlight() {
highlighted = false
}
}
P.S.: For those of you that don't like Storyboards/Xib's, just migrate the implementation of awakeFromNib()
to init()
回答5:
Swift 3 Solution
extension UIControl {
open override func point(inside point: CGPoint, with event: UIEvent?) -> Bool {
let inside = super.point(inside: point, with: event)
if inside != isHighlighted && event?.type == .touches {
isHighlighted = inside
}
return inside
}
}
回答6:
I've run into this numerous times when working with updating older Storyboards to iOS 7+, usually when the ViewController in question has a form of a UIScrollView. Double check these 2 settings in your Storyboard on the ViewController Object (Not the view, the one with the yellow circle). When I unchecked the 'Extend Edges' under top & bottom bars, the scrollView's frame was adjusted down by 64 points (height of Nav & Status bars).
After setting the space between NavBar.bottom and scrollView.top back to 0, the button started working. This might be due to the fact the scrollView.frame.bottom was 64 pixels above the bottom of the window, so touches in that area were disregarded because they were technically out of the scrollView's frame but still displayed visually for some reason.
回答7:
iOS 9.3, Xcode 7.3
I would suggest that you should make a category to the UIButton class that implements Lukas' answer. For instructions on how to create a category see this post: How do I create a category in Xcode 6 or higher?
Give it an appropriate name with the traditional "+" sign, i.e. if you name it "BottomOfScreen", then the resulting file name will be "UIButton+BottomOfScreen".
If you are using objective-c, then you will get a *.h and *.m files with he new category.
*.h
#import <UIKit/UIKit.h>
@interface UIButton (BottomOfScreen)
@end
*.m
#import "UIButton+BottomOfScreen.h"
@implementation UIButton (BottomOfScreen)
- (BOOL)pointInside:(CGPoint)point withEvent:(UIEvent *)event
{
BOOL inside = [super pointInside:point withEvent:event];
if (inside && !self.isHighlighted && (event.type == UIEventTypeTouches))
{
self.highlighted = true;
}
else
{
nil;
}
return inside;
}
@end