Touch Event in Scrollview ios - ios

Scrolling is not stopping when I touch over the contact labels. How can I add this feature for this open project.
https://www.cocoacontrols.com/controls/scroller
If I touch the background, it is working perfectly. I would like to have same thing for the contacts labels too.
Basically, it uses scrollview and there is an animation while scrolling. I can not make stop it when I touch over the labels.
Any help is welcome.

Though I am unfamiliar with the scroller project, maybe this can at least get you on the right path.
The likely reason why touching the contacts isn't stopping the scrolling is because the labels are receiving their own touch events for their own purpose, which is probably the desired behavior, since you would probably want to touch one of the contacts and have it do something. It's possible that since the touch events are being intercepted in that view for that reason, that you can not interact with the scroll view using the same event.
You may need to set the userInteractionEnabled property of the view surrounding each contact to false until the scrollview has stopped scrolling. There are several ways you could do this, but this might be enough to get you started on a good solution.

My condition may be similar with yours.
I build a scroll view in storyboard and a view is added to the scroll view.All of my UI component was placed in the content view including two textfields.Generally speaking, I would like to rewrite the - (void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event method, and end editing actions in this view.
However,rewrite the method in scrollview's superview has little help.But when I subclass the view and rewrite that method in this subclass Every thing is OK.
According to my condition ,subclass the view and rewrite - (void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event.Process the logic in view level.

Related

iOS - Superview gesture recognizer getting called when not wanted, can't cancel child view touch event

I have a super view that has a UITapGestureRecognizer on it. It allows touches within the view because there are clickable items within the view.
When these items are clicked on, I want to take a specific action, not the generic one that covers the entire superview. Unfortunately in my TouchDown event of my child control I don't know how to stop the event here. I know I could create a kludge flag, but this seems like the wrong way to go.
Any advice?
James
OK I got a solution. Totally my problem. I was playing around with trying to get all touches to work and at one point I had set cancelTouchesInView = true on the UITapGestureRecognizer superview. While this didn't stop the other touches from happening, for whatever reason the touches carried through to the superview as well. I understand that this explanation probably makes no sense, but that's what did it. Still trying to wrap my head around how iOS does touch.

iOS - View that handle taps, but let swipes go through to the superview

I have an app with quite a complex UI, there's a big UIView called the cover with a UITableView underneath it. The tableView is configured with a tableHeaderView of the same height as the cover. As the tableView scrolls up, the cover moves up the screen (with various fancy animations) using a UIScrollViewDelegate. To allow users to scroll the tableView by swiping the cover, I've overridden the - (BOOL)pointInside:(CGPoint)point withEvent:(UIEvent *)event method to always return false.
I've now added some UIButton views to the cover. I've managed to make them respond to taps by changing the way I've overriden the pointInside method like this:
- (BOOL)pointInside:(CGPoint)point withEvent:(UIEvent *)event
{
BOOL isInside = [_directionsButton pointInside:[_directionsButton convertPoint:point fromView:self] withEvent:event];
return isInside;
}
The only problem now is that if you start a swipe gesture on the button, it's caught by the button and the tableView doesn't scroll. I want to be able to ignore swipe gestures on the button (so really let them pass to the view below).
Normally, I would just make the cover view the tableHeaderView, which seems to handle this kind of behaviour really well. However, I can't do this here, due to some unique animations done on the cover as the table scrolls.
Did you tried identifying the Gestures using Gesture Recognisers and doing action method that is to be called when the specified gesture is detected?
Please check this link. This may help you for that.

How to make UIView stop receiving touch events?

I'm working on an app where the user is expected to rapidly touch and swipe across multiple UIViews, each of which is supposed to do an action once the user's finger has reached it. I've got a lot of views and so the typical thing to do, where I'd iterate over each view to see if a touch is inside of its bounds, is a no-go - there's just too much lag. Is there any other way to get touch events from one view to another (that is beside the first one)? I thought maybe there is some way to cancel the touch event, but I've searched and so far have come up empty.
One of the big problems I have is that if I implement my touch handling in my view controller, touchesBegan only fires for the first touch - if the user touches something and then, without moving the first finger, taps on something else, that tap is not recorded in either touchesBegan or touchesMoved. But if I implement my touch handling in the UIViews themselves, once a view registers a touch, if the user does not lift their finger up and moves it, the views around the first view do not register the touch. Only if the user lifts his finger and then puts it back down will the surrounding views register the touch.
So my question is, lets say I have two views side by side, my touch handling code is implemented in the views, and I put my finger down on view 1. I then slide my finger over to view 2 - what do I need to do to make view 2 register that touch, which started in view 1 and never "ended"?
Set userInteractionEnabled property of UIView to NO.
view.userInteractionEnabled = NO;
UIView has the following property:
#property(nonatomic, getter=isUserInteractionEnabled) BOOL userInteractionEnabled
Ok, I figured out what was going on. Thing is, I have my views as subviews of a scrollview, which is itself a subview of my main view. With scrollEnabled = NO, I could touch my subviews - but apparently the scrollview was only forwarding me the initial touch event, and all subsequent touches were part of that initial event. Because of that, I had many weird problems such as touching two views one after the other, both would select and highlight, but if I took the first finger off the screen both views would de-select. This was not the desired behavior.
So what I did is I subclassed the scrollview and overrode the touch handling methods to send the events to its first responder, which is its superview, which is the view where I'm doing my touch handling. Now it works!

Should "clickable" areas in iOS be buttons?

Should "clickable" areas in iOS be buttons or is it ok to just use a generic UIView, UIImage and so on?
Say i have a block of text with an icon, borders, shadows and so on. It looks like a bilboard. What would be the best way to implement that? Using a custom UIButton and just add subviews to it or creating just a generic UIView?
Any thoughts appreciated!
You can simply add UIGestureRecognizers to your UIView and handle them. You can find the documentation here and a tutorial here.
Probably for a view containing multiple subviews, you want to use a UIView subclass. While a UIButton would be OK for adding views, state changes, enabling/disabling may do wonky things to the view as a whole (including the subviews). Using your own UIView subclass will ensure that what gets displayed doesn't get toyed around with by any state changes, giving you complete control. You can override
- (void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event
- (void)touchesEnded:(NSSet *)touches withEvent:(UIEvent *)event
to intercept touches on your custom view. If you're going to do this, remember that the userInteractionEnabled field MUST be set to true.
An additional note: you mentioned shadows as one of the elements in your question. If you're using CALayer to do this, definitely avoid using UIButton, as it's set of layers to handle different states is quite complex.
If the target area is big enough, you could place a transparent UIButton (switch the button type to custom, but don't supply an image) over the top of the clickable view to intercept the taps.

Custom UIGestureRecognizer Not Working As Expected

I have a UITableView which I present in a UIPopoverController. The table view presents a list of elements that can be dragged and dropped onto the main view.
When the user begins a pan gesture that is principally vertical at the outset, I want the UITableView to scroll as usual. When it's not principally vertical at the outset, I want the application to interpret this as a drag-and-drop action.
My unfortunately lengthy journey down this path has compelled me to create a custom UIGestureRecognizer. In an attempt to get the basics right, I left this custom gesturer as an empty implementation at first, one that merely calls the super version of each of the five custom methods Apple says should be overridden:
(void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event;
(void)touchesMoved:(NSSet *)touches withEvent:(UIEvent *)event;
(void)touchesEnded:(NSSet *)touches withEvent:(UIEvent *)event;
(void)touchesCancelled:(NSSet *)touches withEvent:(UIEvent *)event;
(void)reset;
This results in nothing happening, i.e. the custom gesture's action method is never called, and the table view scrolls as usual.
For my next experiment, I set the gesture's state to UIGestureRecognizerStateBegan in the touchesBegan method.
This caused the gesture's action method to fire, making the gesture appear to behave just like the standard UIPanGestureRecognizer. This obviously suggested I was responsible for managing the gesture's state.
Next up, I set the gesture's state to UIGestureRecognizerStateChanged in the touchesMoved method. Everything still fine.
Now, instead, I tried setting the gesture's state to UIGestureRecognizerStateFailed in the touchesMoved method. I was expecting this to terminate the gesture and restore the flow of events to the table view, but it didn't. All it did was stop firing the gesture's action method.
Lastly, I set the gesture's state to UIGestureRecognizerStateFailed in the touchesBegan method, immediately after I had set it to UIGestureRecognizerStateBegan.
This causes the gesture to fire its action method exactly once, then pass all subsequent events to the table view.
So...sorry for such a long question...but why, if I cause the gesture to fail in the touchesBegan method (after first setting the state to UIGestureRecognizerStateBegan), does it redirect events to the table view, as expected. But if I try the same technique in touchesMoved (the only place I can detect that a move is principally vertical), why doesn't this redirection occur?
Sorry for making this more complicated than it actually was. After much reading and testing, I've finally figured out how to do this.
First, creating the custom UIGestureRecognizer was one of the proper solutions to this issue, but when I made my first test of the empty custom recognizer, I made a rookie mistake: I forgot to call [super touches...:touches withEvent:event] for each of the methods I overrode. This caused nothing to happen, so I set the state of the recognizer to UIGestureRecognizerStateBegan in touchesBegan, which did result in the action method being called once, thus convincing me I had to explicitly manage states, which is only partially true.
In truth, if you create an empty custom recognizer and call the appropriate super method in each method your override, your program will behave as expected. In this case, the action method will get called throughout the dragging motion. If, in touchesMoved, you set the recognizer's state to UIGestureRecognizerStateFailed, the events will bubble up to the super view (in this case a UITableView), also as expected.
The mistake I made and I think others might make is thinking there is a direct correlation between setting the gesture's state and the chronology of the standard methods when you subclass a gesture recognizer (i.e. touchesBegan, touchesMoved, etc.). There isn't - at least, it's not an exact mapping. You're better off to let the base behavior work as is, and only intervene where necessary. So, in my case, once I determined the user's drag was principally vertical, which I could only do in touchesMoved, I set the gesture recognizer's state to UIGestureRecognizerStateFailed in that method. This took the recognizer out of the picture and automatically forwarded a full set of events to the encompassing view.
For the sake of brevity, I've left out a ton of other stuff I learned through this exercise, but would like to point out that, of six or seven books on the subject, Matt Neuburg's Programming IOS 4 provided the best explanation of this subject by far. I hope that referral is allowed on this site. I am in no way affiliated with the author or publisher - just grateful for an excellent explanation!
That probably happens because responders expect to see an entire touch from beginning to end, not just part of one. Often, -touchesBegan:... sets up some state that's then modified in -touchesMoved..., and it really wouldn't make sense for a view to get a -touchesMoved... without having previously received -touchesBegan.... There's even a note in the documentation that says, in part:
All views that process touches,
including your own, expect (or should
expect) to receive a full touch-event
stream. If you prevent a UIKit
responder object from receiving
touches for a certain phase of an
event, the resulting behavior may be
undefined and probably undesirable.

Resources