I made a widget for the iOS 5 notification center that implements a UISlider along with a UITapGestureRecognizer.
The gestureRecognizer works fine, but the UISlider is very unresponsive and will only move a small amount if the thumb is touched and dragged.
It will not move until another touch down. Is there a way to circumvent this limitation? (I didn't load any views above it, it works fine on the iPhone and iPod Touch).
I finally found a way to fix it.
You should set the superviews gesturerecogniser's cancelTouchesInView property to NO on viewWillAppear.
- (void)viewWillAppear {
if (deviceIsIPAD()) {
UIView *list = [[objc_getClass("SBBulletinListController") sharedInstance] listView];
for (UIGestureRecognizer *gr in list.gestureRecognizers) {
gr.cancelsTouchesInView = NO;
}
}
}
Related
I try to insert UISlider to UITableViewCell, but swipe gesture doesn't work correctly. For sliding needed hold and move the thumb, but i want to get swipe gesture without holding. I think tableview's own gestures not allow do this, but i don't know how to disable it.
Use - gestureRecognizer:shouldRecognizeSimultaneouslyWithGestureRecognizer:
to set the property to YES. Then you can add a check in a function to decide which gesture to act on.
I've encountered the same problem recently. It happens for me in static cell of UITableViewController, it's instantiated from storyboard. I have found an ugly workaround, but will be happy to see a better solution for this.
So i've disabled all gesture recognisers of self.view and self.view.superview of UITableViewController:
- (void)disableGestureRecognisersInView:(UIView*)view {
for ( UIView *subview in view.subviews ) {
for ( UIGestureRecognizer *rec in subview.gestureRecognizers ) {
rec.enabled = NO;
}
}
- (void)viewWillAppear:(BOOL)animated {
[super viewWillAppear:animated];
[self disableGestureRecognisersInView:self.view];
[self disableGestureRecognisersInView:self.view.superview];
}
And now UISlider works normally, didn't noticed any other problems because of this workaround too. But i still don't like it.
here's an odd one..
I've got a UIView xib file that looks like this:
I've connected every UIButton touchDown and touchUpInside events to two IBAction methods:
- (IBAction)touchUpInside:(id)sender
{
NSLog(#"touch up inside");
if (((UIButton *)sender == _enter) | ((UIButton *)sender == _back)) {
[(UIButton *)sender setBackgroundColor:_color2];
}
else {
[(UIButton *)sender setBackgroundColor:_color1];
}
}
- (IBAction)touchDown:(id)sender
{
NSLog(#"touch down");
[(UIButton *)sender setBackgroundColor:_color2];
}
Everything works except for the bottom-most row of UIButton's, that's the odd part:
The touch down event is fired, but the button must be held for 0.5 second for it to change background color, whereas it is instantaneous for the other buttons.
It ONLY happens for the bottom-most row of UIButton's, as I've tried to switch buttons 7, 8, 9 with buttons #back, 0, #enter like this:
I've checked in Interface Builder all the UIButton attributes are the same, and I've tried moving the UIButton's objects order around as you can see on the left side of the picture, and I'm about out of ideas already. Basically what's odd is the UIControl behavior differs based on its position on the parent view...
UPDATE: I made the parent UIView height value large enough that there is 50 free pixels below the last row and the UIButton's work fine now. The only reason I can think of now is that there is a UITabBar there 2 view controllers level underneath. Even so it doesn't make sense.
The document says:
Expect users to swipe up from the bottom of the screen to reveal
Control Center. If iOS determines that a touch that begins at the
bottom of the screen should reveal Control Center, it doesn’t deliver
the gesture to the currently running app. If iOS determines that the
touch should not reveal Control Center, the touch may be slightly
delayed before it reaches the app.
One solution is here:
UIButton fails to properly register touch in bottom region of iPhone screen
But, in your case, I think you should use inputView in UIResponder.
See: https://developer.apple.com/library/ios/documentation/StringsTextFonts/Conceptual/TextAndWebiPhoneOS/InputViews/InputViews.html
The inputView is not affected by that problem.
I have a custom UICollectionViewCell that has a few custom UIView objects residing inside them. Each of these UIViews has a UIButton which responds to Touch Down and Touch Up Inside linked by IBActions. Basically, I want these buttons to shrink down when pressed down and spring back to their original size when let go. I can easily accomplish this with the controls and the press down and press up works. However, the problem I am facing happens when scrolling is introduced into the mix. The UICollectionView these cells are apart of is a scrolling one. If I happen to touch a button as I start my scroll, the Touch Down event is triggered as well as the scrolling event of the UICollectionView. If I recall correctly, this was never the case pre-iOS7. When a scrolling event was started, the UIButton event wasnt fired off, I think it had to do with the delaysContentTouches. This looks to be broken or changed now. It actually still works decently on iPhone, just not on iPad. If I scroll my view on iPad, with my touch starting inside the embedded UIButton, the button will shrink and the buttons action will be fired off.
So to restate the issue as plainly as I can: Is there anyway to ignore touches on embedded UIButtons when scrolling is occurring? Touches work fine when there is no scrolling triggered, I just dont want the events to fire off if the user is indeed scrolling. Is there any workaround?
If you need any more specific details, I would be happy to help you understand.
you need to subclass scrollView (collectionView or tableView) and override
- (BOOL)touchesShouldCancelInContentView:(UIView *)view {
if ([view isKindOfClass:UIButton.class]) {
return YES;
}
return [super touchesShouldCancelInContentView:view];
}
swift
override func touchesShouldCancelInContentView(view: UIView) -> Bool {
if view is UIButton {
return true
}
return super.touchesShouldCancelInContentView(view)
}
thats it now you can scroll over button and not lose button tap event.
In a UICollectionView of mine, buttons inside of UICollectionViewCells registered TouchUpInside-taps even though the UICollectionView was still decelerating, which sounds like a similar problem to what you're having. I created a UIButton subclass that overrides beginTrackingWithTouch:withEvent and will return NO in case the UIScrollView it's contained in is decelerating or dragging.
- (BOOL)beginTrackingWithTouch:(UITouch *)touch withEvent:(UIEvent *)event
{
UIView *superView = self;
while((superView = [superView superview])) {
if ([superView isKindOfClass:UIScrollView.class]) {
UIScrollView *scrollView = (UIScrollView *)superView;
if (scrollView.isDecelerating || scrollView.isDragging) {
return NO;
}
}
}
return [super beginTrackingWithTouch:touch withEvent:event];
}
The easiest thing to try that comes to mind is to check if the UIScrollView (your UICollectionView) is scrolling or dragging when the button action is triggered.
if(! self.collectionView.dragging && ! self.collectionView.decelerating)
{
// do action because we are not moving
}
Have you tried that?
I have UIScrollView loaded with UIButtons and on UIButton action I have highlighted UIImage of each UIButton.
If I don't set delaysContentTouches as NO then highlighted UIImage of UIButton will not shown if I touch up UIButton very fast. After I set delaysContentTouches property as NO then only UIButton highlighted UIImage is shown.
Now after setting delaysContentTouches property as NO for UIScrollView. I can not scroll my UIScrollView by dragging on the UIButtons. Now how can I resolve this issue.
Please give me an advise.
Thanks in advance.
Here's what works for me. Subclass UIScrollView, and implement only this method:
- (BOOL)touchesShouldCancelInContentView:(UIView *)view {
return YES;
}
Then set delaysContentTouches = NO;
Voila! Works just like the home screen: Highlights buttons immediately, but still allows scrolling :)
I found that in iOS 8, the UIScrollView's underlying UIPanGestureRecognizer is not respecting the UIScrollView's delaysContentTouches property. I consider this an iOS 8 bug. Here's my workaround:
theScrollView.panGestureRecognizer.delaysTouchesBegan = theScrollView.delaysContentTouches
OK I have resolved by implementing below method :
- (BOOL)touchesShouldCancelInContentView:(UIView *)view
{
NSLog(#"touchesShouldCancelInContentView");
if ([view isKindOfClass:[UIButton class]])
return NO;
else
return YES;
}
Unable to find a satisfactory solution online so far (and it seems to be that Apple is ignoring the issue). Found a thread on Apple's developer forum with some suggestions in there that may help: UIScrollView: 'delaysContentTouches' ignored
I was able to use the workaround from this link. To summarize the workaround (I'm para-quoting here):
UIEvent objects contain a time stamp.
You can record the time stamp at the time of touchesBegan on your
embedded subview.
In touchesMoved of scrollView's subview, look at the time stamp and
location again.
If the touch has not moved very far and more than, say, 0.1 seconds
have passed, you can assume the user touched the subview and then
delayed movement.
In this case, the UIScrollView will have decided, independently, that
this is NOT a scrolling action even though it will never tell you
that.
So, you can have a local state variable to flag that this condition of
delayed movement occurred and process events received by the subview.
Here's my code:
-(void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event{
// store the timestamp
_beginDragTimeStamp = event.timestamp;
// your embedded subview's touches begin code
}
-(void)touchesMoved:(NSSet *)touches withEvent:(UIEvent *)event{
// compare and ignore drag if time passed between tap and drag is less than 0.5s
if(event.timestamp - _beginDragTimeStamp < 0.5) return;
// your drag code
}
I had same issue & same hierarchy of the views, With latest sdk , just use it :
Setting delaysContentTouches to NO for UIButton in the same UITableViewCell.
self.scrollview.delaysContentTouches = NO
Create a subclass of the UIScrollView (or UITableView, or UICollectionView, or any other UIScrollView subclass that you use).
Implement the below method:
- (BOOL)touchesShouldCancelInContentView:(UIView *)view {
if ([view isKindOfClass:UIButton.class]) {
return YES;
}
return [super touchesShouldCancelInContentView:view];
}
Set this subclass at xib/storyboard as a "Custom Class" class if you use the interface builder.
Unselect Delay Touch Down in a xib or set delaysContentTouches = NO in code.
I'm trying to fix a bug that involves UIView hitTest:withEvent: being called on my view when the touches are on the UIKeyboard, but only after the app has been in the background.
It was occurring in my app with a complex view hierarchy so I reproduced it in an app with only 2 views:
1 UIView 768x1024 (fullscreen)
1 UITextView 200x200 in the upper half of the fullscreen view
The behavior is as follows:
Tapping the textview causes the fullscreen view's hitTest method to fire, the textfield becomes first responder, and then the keyboard appears all as expected. Tapping on keyboard keys works fine.
Now dismiss the keyboard.
Send the app to the background.
Then resume the app.
Make the textview first responder again. Here's the trouble, now when tapping keys on the keyboard the fullscreen view's hitTest method is firing.
I'm seeing this on an iOS 5 iPad 2. Only on device though, never in the simulator. Any idea why hitTesting could get messed up in this way? Thanks.
The issue described above seems to be caused by the keyboard's UIWindow getting stuck in a bad state. Ensuring that the keyboard window's hidden property gets set to YES (even if it is already YES) fixes the problem for me. This can be done in your UIApplicationDelegate class:
- (void)applicationWillEnterForeground:(UIApplication *)application {
// The keyboard sometimes disables interaction when the app enters the
// background due to an iOS bug. This brings it back to normal.
for (UIWindow *testWindow in [UIApplication sharedApplication].windows) {
if (!testWindow.opaque && [NSStringFromClass(testWindow.class) hasPrefix:#"UIText"]) {
BOOL wasHidden = testWindow.hidden;
testWindow.hidden = YES;
if (!wasHidden) {
testWindow.hidden = NO;
}
break;
}
}
}
The class name of the keyboard window, at least in iOS 5 with a standard US keyboard, is UITextEffectsWindow. As usual it is not a good idea to rely on undocumented class names but in the case of an OS-specific bug it works for my purposes. There could be any number of windows, including the root application window, keyboard, alerts, and other windows that your app or other frameworks have added, so don't be too inspecific.
Got the same issue here. It does happen ONLY when I hit home and return to the app.
Does not happen in the first fresh run.
And it is related to iOS5 as well.
I got the same problem, and my work around is to listen to UIKeyboardDidShowNotification and UIKeyboardDidHideNotification, calculate the keyboard height using UIKeyboardFrameEndUserInfoKey, then in my hitTest:withEvent: method I would see whether the hit was on the keyboard "zone" or not.
Just to expand on the answer by #enzo-tran , this is what I ended up doing: I added a keyboardRect property to my UIView subclass, registered for UIKeyboardDidShowNotification and UIKeyboardDidHideNotification, and added:
- (UIView *)hitTest:(CGPoint)point withEvent:(UIEvent *)event {
if (CGRectContainsPoint([self keyboardRect], point)) {
// Ignore
} else {
...
}
}
- (void)keyboardDidShow:(NSNotification *)notif {
CGRect keyboardRect;
[[[notif userInfo] valueForKey:UIKeyboardFrameEndUserInfoKey] getValue:&keyboardRect];
keyboardRect = [self convertRect:keyboardRect fromView:nil];
[self setKeyboardRect:keyboardRect];
}
- (void)keyboardDidHide:(NSNotification *)notif {
[self setKeyboardRect:CGRectZero];
}