Why a UIButton on UITableViewCell only drawn darker when touch gesture continued for a short time - ios

Pressing the button quickly and not holding for a short time, will not highlight the button.
Different from a UIButton on a common UIView.
Like the head photo in official Twitter client got same issue.
Instagram client seems solved this, all buttons works fine.
Find same question here:
Why doesn't UIButton showsTouchWhenHighlighted work when the button is on a UITableViewCell?
But I still don't know how to fix it.

Well... a UITableView is a subclass of UIScrollView and the UIScrollView class is known to eat touches for it's own purpose.
When it realizes the touch was not meant for it, it passes it to it's immediate subview.
This feature is the delaysContentTouches property (which by default is YES).
Which is why, the UIButton shows it's highlighted state only after a extended touch because the touch event was with the UITableView for a short while until it determined whether the touch was meant for scrolling or swiping the cell and on realizing the touch was for neither, it immediately passes the touch event to the subView directly below it.
In case of a quick-tap, the button's highlighted state is bypassed due to this delay and the target selector method is called directly.
To show the highlighted state of the button in a UITableView (just as it would on a UIView) do:
For iOS7+:
In -viewDidLoad or anywhere appropriate do:
[yourTableViewObject setDelaysContentTouches:NO];
Also... The cell.subviews has a class UITableViewCellScrollView which apparently is another scrollView and we need to disable the delaysContentTouches property of this class as well.
So... in the -cellForRowAtIndexPath: method (just before return cell;) do:
NSArray *test = cell.subviews;
for (UIView *currentView in cell.subviews) {
if ([NSStringFromClass([currentView class]) isEqualToString:#"UITableViewCellScrollView"]) {
UIScrollView *svTemp = (UIScrollView *) currentView;
[svTemp setDelaysContentTouches:NO];
break;
}
}
For iOS 6-:
In iOS6, the cell.subviews has a UITableViewCellContentView class which is not a scrollView subclass and so all it takes is setting one parameter for the tableView alone.
So, in -viewDidLoad or anywhere appropriate, this is all that you need:
[yourTableViewObject setDelaysContentTouches:NO];
PS: By doing this, it will mess up with the scrolling of the tableView so use your better judgement.

Related

UIPageViewController crashes when holding down a button while it's animating

I have UIPageViewController that animates programatically. The problem is that the view controllers inside it has UIButtons inside them. When I hold down a button and wait until the UIPageViewController animates, the app crashes with the error:
'Failed to determine navigation direction for scroll'
What I think I need to do is to somehow fake that the user releases the button before the UIPageviewController animates.
However, [self.button sendActionsForControlEvents:UIControlEventTouchCancel]; doesn't seem to do the trick. Neither do UIControlEventTouchUpInside.
Is there a better way do to it or am I using sendActionsForControlEvents wrong?
All sendActionsForControlEvents: does is call any methods you've assigned to the control events passed in for the button. It doesn't call any internal methods to programmatically lift up touches or anything like that.
Right before you programmatically animate your page view controller, try using this method to effectively cancel any touches on the pan gesture recognizer of the page view controller's internal scroll view:
- (void)cancelPanGestureTouchesOfPageViewController:(UIPageViewController *)pageVC
{
// Since UIPageViewController doesn't provide any API to access its scroll view,
// we have to find it ourselves by manually looping through its view's subviews.
for (UIScrollView *scrollView in pageVC.view.subviews) {
if ([scrollView isKindOfClass:[UIScrollView class]]) {
// We've found the scroll view, so use this little trick to
// effectively cancel any touches on its pan gesture recognizer
BOOL enabled = scrollView.panGestureRecognizer.enabled;
scrollView.panGestureRecognizer.enabled = !enabled;
scrollView.panGestureRecognizer.enabled = enabled;
}
}
}
(Note that this is messing with the internal view hierarchy of UIPageViewController, so this method is kind of ugly and may break in the future. I generally don't recommend doing stuff like this, but I think in this instance it should be okay.)

UITapGestureRecognizer on UIView and Its Subview Respond Together When Subview Is Tapped

UITapGestureRecognizer is applied to both UIImageView and its subview (UITextView). However, when I tap on subview, the receiver becomes subview and its parent view (i.e. UIImageView + UITextView). It should however be only subview because that was the one I tapped. I was assuming nested gestures would react first but apparently parent receives the fist tap and then it goes to child.
So, there are different solutions out there for various scenarios (not similar to mine but rather buttons inside scroll view conflict). How can I easily fix my issue without possible subclassing and for iOS 6+ support? I tried delaying touch on start for UIGestureRecognizer on UIImageView and I tried setting cancelsTouchesInView to NO - all with no luck.
Try the following code:
conform the <UIGestureRecognizerDelegate> to your class.
set yourGesture.delegate = self;
then add this delegate Method:
- (BOOL)gestureRecognizer:(UIGestureRecognizer *)gestureRecognizer shouldReceiveTouch:(UITouch *)touch {
// return YES (the default) to allow the gesture recognizer to examine the touch object, NO to prevent the gesture recognizer from seeing this touch object.
if([touch.view isKindOfClass: [UITextView class]] == YES)] {
return YES;
}
else {
return NO;
}
}
Hope it will solve your issue. Enjoy Coding..!!!!
That's exactly what is it supposed to do.
View hierarchy is like a tree structure and its traversal during a touch gesture starts from the root node. It is very likely for your parent view to receive gesture first and then its subviews. The traversal skips the nodes for which
userInteractionEnabled = NO.
since, you don't have any code I can't help you to play with this flag. A more general solution is to always set gesture only for your parentView and in the gesture delegates check the coordinates if it belongs to any one of the subview and if yes then call your gesture method for your subview.
Not a clean approach but works. !!
you should implement the UIGestureRecognizer delegate methods and apply the correct policy to the gesture, when multiple gesture are recognized

UITableView didSelectRowAtIndexPath Not Selected on UIPageViewController

I have a UIViewController that has UIPageViewController's view added to it. That UIPageViewController has a page with a subview that has a UITableView added as a subview. I am using iOS 6 and the method didSelectRowAtIndexPath is not being called when I click on the cell. There is a weird "bug" though... If I turn the page halfway and then come back to the page I was on (without completing the page turn), I'm then able to select the cell. I assume this has something to do with gesture recognizers, but I can't figure it out. I tried removing the gesture recognizers from the instantiation of the UIPageViewController, but was unable to get that to work.
I remove the tap gesture recognizers from the UIPageViewController like so, but still the buggy behavior exists...
for (UIGestureRecognizer *recognizer in pageViewController.gestureRecognizers) {
if ([recognizer isKindOfClass:[UITapGestureRecognizer class]]) {
recognizer.enabled = NO;
}
}
Any suggestions?
Subclass UIView with UITableView. let say myUIView.
Implement the UITableView delegates in myUIView.
Add myUIView as a subview of UIPageControl.
UITableView delegates in myUIView will trigger.
It turned out to be something very simple. On my UIViewController I had the autoresizingMask set to FlexibleHeight. Changing it to None for some reason fixed my problem.
// self.contentView.autoresizingMask = UIViewAutoresizingFlexibleHeight;
// Changed to :
self.contentView.autoresizingMask = UIViewAutoresizingNone;
If anyone can shed some light onto the reasons why this is the case, I'd appreciate it.

Detect touch event on UIScrollView AND on UIView's components [which is placed inside UIScrollView]

I have UIViewController on my simple storyboard IPad project which contains UIScrollView which is placed over entire surface (1024 x 768). I have created 3 XIB files which are UIViews which my application loads on start in viewDidLoad and add them into UIScrollView. Each of these 3 XIB files contains only one UIButton.
This is hierarchy:
~ UIViewController (UIViewControllerClass is class for this
UIViewController)
~~ UIScrollView (contains 3 identical UIViews)
~~~ UIView (UIViewClass is File's Owner for this XIB file)
~~~~ UIButton
I would like that my UIViewControllerClass becomes aware of both: touch anywhere on UIScrollView component AND if UIScrollView is touched, if UIButton inside of UIView in UIScrollView is touched, to have information that exactly that button is touched.
I made IBAction inside UIViewClass for touch on UIButton inside UIView in UIScrollView and when I set User Interaction Enabled = YES on all elements (UIViewController, UIView and UIScrollView) this method is called as it should.
But at this point my UIViewControllerClass isn't aware that touch occurred inside UIScrollView on that UIButton. I made touch recognizer like this:
UITapGestureRecognizer *touch = [[UITapGestureRecognizer alloc] initWithTarget:self action:#selector(handleTouch)];
touch.numberOfTouchesRequired = 1;
and added it to UIScrollView component. In this way I am able to detect touch event on UIScrollView component in UIViewControllerClass, but touch event handler for UIButton in UIView which is inside UIScrollView isn't called anymore.
So, I need to have these two informations in UIViewControllerClass:
Touch on UIScrollView component was made
Touch on UIButton in UIView which is inside UIScrollView (if this button was touched) was made
I suppose that attaching touch event recognizer to entire UIScrollView component isn't solution, since it disables all touch event handlers I wrote inside UIViewClass.
I think solution is that somehow touches which are made on components in UIView inside UIScrollView should be sent up to UIViewControllerClass, but I didn't found a way to do this.
If anyone can help me, I'd be very grateful. Thanks in advance.
[edit #1: ANSWER by Zheng]
Tap gesture must have cancelsTouchesInView option set to NO!
For my case above, this line solves everything:
touch.cancelsTouchesInView = NO;
Many thanks to Zheng.
I don't know if this works for you or not, but I've given an answer about touch events for views inside scrollview here:
Dismissing the keyboard in a UIScrollView
The idea is to tell the scrollView not to swallow up all tap gestures within the scroll view area.
I'll paste the code here anyways, hopefully it fixes your problem:
UITapGestureRecognizer *tapGesture = [[UITapGestureRecognizer alloc] initWithTarget:self action:#selector(hideKeyboard)];
// prevents the scroll view from swallowing up the touch event of child buttons
tapGesture.cancelsTouchesInView = NO;
[pageScrollView addGestureRecognizer:tapGesture];
[tapGesture release];
...
// method to hide keyboard when user taps on a scrollview
-(void)hideKeyboard
{
[myTextFieldInScrollView resignFirstResponder];
}
You can subclass your UIScrollView and override the method - hitTest:withEvent: which is called by the system to determine which view will handle the event. Whenever it is called, you can assume that a touch event occurred inside the scroll view, and by calling the super implementation, you can get the view which would normally process the event.
you can capture any kind of gestures in the UIscrollView. Make sure you also handle some of the default properties as well like set cancelsTouchesInView property to false, it is true by default. Also give some tag nos to your sub views to distinguish in selectors. & also enable their User interaction to true.
let tap = UITapGestureRecognizer(target: self, action:
selector(didTapByUser(_:)))

UIWebView overriding touch events in my UITableViewCell subclass

I have a UIWebView in a UIView subclass stored in a UITableViewCell subclass. I've noticed that I lose touch events in my view controller. How can I get around this?
I've tried
- (BOOL) canResignFirstResponder {
return YES;
}
in my custom UIView. I want the UIWebView to receive touch input when the one div is selected. I thought about making the div 100% high but that causes other problems. I was thinking of allowing the UIView subclass to resign first responder status so that tableView:didSelectRowAtIndexPath would be called but doesn't seem to work.
Any suggestions?
Have you tried using the gesture recognizer delegate method gestureRecognizer:shouldRecognizeSimultaneouslyWithGestureRecognizer:? You can read about it here: http://developer.apple.com/library/ios/#documentation/uikit/reference/UIGestureRecognizerDelegate_Protocol/Reference/Reference.html

Resources