Catch touch events anywhere onscreen in certain views? - objective-c

I have a menu which I'd like to have automatically hide if it's inactive after a certain amount of time. This menu is composed of a hierarchy of UIViewControllers, which present various different views.
I'm thinking along the lines of running a timer, which invalidates and starts over whenever there's a touch.
Is it possible to catch all touch events in a set of UIViews? Perhaps just keep a boolean lying around and use the main UIWindow to catch touch events?
EDIT:
My app is a kiosk app of sorts, with a main screen and a menu. When the menu is up, I want it to run an auto dismiss timer, which resets after any touch in the entire menu screen. The menu is displayed over the entire screen, modally.

One way to be sure is to subclass UIApplication and override - (void)sendEvent:(UIEvent *)event method, every touch event happening in your app goes through this method and you can check the UIEvent type to see if it's UIEventTypeTouches and reset the timer.
Another way to do this simply involves adding a transparent layer over whole user accesible UI and override hitTest:withEvent:.

You can have an invisible view on top of your modals view controllers, and put have either a gesture recognizer on it which can start a timer, either a
-touchesBegan:withTouches
method, and then send to the .nextResponder the same method.

Related

Stop a UIGestureRecognizer from operating on UIButtons inside a UIView

I've attached a UITapGestureRecognizer to a UIView in my application. If the user double-taps it, the buttons inside that view get randomly re-arranged. Working fine, lovely.
However, the user can also trigger this by double-tapping one of the buttons themselves, or even by tapping two buttons on different parts of the screen.
Is there a sensible / easy way to have this double-tap only work if the two taps are within x number of pixels, and on the view itself, not any elements within it such as these UIButtons?
I think the usual way to do this is with shouldReceiveTouch. Check out this question for a lengthy discussion and all the details.
One way to do this would be to attach a single tap gesture recognizer to the buttons -- this will preempt the button's normal touch events, so you would have to put the button's action method in the gesture recognizer's action method. Then, you would add a dependency to the double tapper to have it only fire if the single tapper fails:
[self.doubleTapper requireGestureRecognizerToFail:self.tapper];

Handle tap event by subview of UIScrollView while scrolling

I have custom UIScrollView subclass with some content views inside. In some of them I have UITapGestureRecogniser. All works fine when scroll view is not scrolling. But when it scrolling content views does not receive tap action. What is the simplest solution to handle tap action by subview while scroll view is scrolling?
Details:
MyScrollView scrolls horizontally. It contains a lot of content views (e.g. MyContentView). Each MyContentView has width about one third of MyScrollView width. So there are about 3-4 visible MyContentView elements at a moment. The main behavior of MyScrollView is to 1)make sure that after scrolling one of MyContentView elements will be at center of screen and 2)to scroll to center of MyContentView if user taps on it. So the main answer I hope to get is how to "properly" implement handling of tap action in MyContentView while MyScrollView is decelerating.
I found some same questions and answers but none of them satisfied me. The best was to implement gestureRecognizer:shouldRecognizeSimultaneouslyWithGestureRecognizer: of UITapGestureRecogniser delegate. But in this case I sometimes (when I tap, make smaaaal drag and release finger so tap is steel recognizable(lets called it quasi tap)) have both tap and scroll events and it leads to bugs for me even if scroll view is not scrolling when I begin tap. When user make quasi tap my application tries to scroll to tapped MyContentView element and than immediately handle normal scrolling. It seems even more terrible, due to some other functionality start to perform after handling tap (it must not perform when normal scrolling).
I need solution where scroll view wait enough to decide it is not tap event and only then make scroll. Otherwise if tap event had recognized scroll must not happen.
You can go with the custom delegates methods as well, using #protocol. Implement those delegate methods in view controller where your UIScrollView has been added.
like in MyContentView:
In touchesBegan method,
[self.delegate contentViewTapped:self];
Now in ContainerView class where scroll view is added, implement that method:
- (void)contentViewTapped:(MyContentView *)myContentView {
NSLog (#"ContentView no: %d", myContentView.tag); // if tag has been set while adding this view to scrollview.
}
Go through the examples for #protocol.
Hope this is what you required.
Enjoy Coding :)
This is built into UIScrollView - take a look at the delaysContentTouches and canCancelContentTouches properties. This should alleviate the problem when dragging a small bit after a tap.
This is all system built-in behaviour. I would suggest sticking with what Apple has provided for the feel of your interface (how it reacts to small drags, for instance) so that your app doesn't feel out of place on a user's phone.
EDIT:
Alternatively, you could disable scrolling of your scroll view in you gesture recognizer and re-enable it once it's ended/cancelled.
Further Edit:
I don't understand - I've created a sample project that illustrates how to intercept touches in a subview of a scroll view using gesture recognizer delegate methods. Play close attention to the "Cancellable Content Touches" and "Delays Content Touches" properties of the scroll view. They're both YES for very important reasons.
You scroll view should be delaying content touches until it has determined if the user is attempting a tap, pseudo-tap (as you put it), or a pan for the scroll view. Apple has already written the functionality you're trying to build; UIScrollView will already do what you want.
The problem is that the system doesn't want a scroll view's subviews intercepting tap events while the scroll view is scrolling. To this end, it cancels touch events if it determines that the user is actually trying to pan. Setting "Delays Content Touches" enables this behaviour. Ensure it's turned on and you should be fine.

How to create a button that spawns a movable image? iOS 5

I would like to create a button in my UIView that when touched spawns another button under the finger which immediately starts to follow the finger.
I have seen a tutorial here that deals with button following finger, but i can not figure out how to make this button follow finger WITHOUT user tapping it after creation.
Using iOS 5.1 SDK with XCode 4.3.2
Any help?
You can add target/action pairs to a button for various control events: to make something immediately start following your finger, you probably want UIControlEventTouchDown.
Hook up your first button to call a method on your view controller for that control state. In that action method, create your new button, and shove it in a property on your view controller so that you can refer to it later. Also set a BOOL property on your view controller that's called something like shouldTrackButton.
To make the new button follow your finger, I guess the easiest way is a pan gesture recogniser on the superview. Set that up to call a method on your view controller. In that method, if the gesture recogniser's state is UIGestureRecognizerStateChanged, call translationInView: on the gesture recogniser, passing in the view controller's view (i.e. the superview of the buttons). Now, if and only if shouldTrackButton is YES, take the frame of the original button, translate it by that amount (using CGRectApplyAffineTransform and CGAffineTransformMakeTranslation), and set the new button's frame to the result.
Finally, in the same method, if the gesture recogniser's state is UIGestureRecognizerStateEnded, set shouldTrackButton to NO.

How to suppress virtual Keyboard slide-in animation?

I've got a problem with creating a modal search view that emulates the behaviour of that of the Weather app. Specifically, there are two animations, that are bothering me and introduce unneeded 0.2 s delays:
When the modal view becomes visible, I give focus to the UISearchDisplayController.searchBar by caling becomeFirstResponder in viewDidAppear. However, the keyboard is not visible, when the modal view has slid in, but needs another 0.2s to slide in after the animation of tehe modal view transition is complete. Moving the call to another callback like viewWillAppear or viewDidLoad did no good, the keyboard won't show up in the first place.
When the user touches cancel, there is another animation taking place, before the delegate's searchDisplayControllerDidEndSearch method is called, expanding the search text field and "melting" away the button. Again, this animation is unneded as the modal view is supposed to transition out when the button is touched.
Additionally, when I dismiss and re-present the same view, not only does the keyboard slide in after the transition, but the cancel button does the same (luckily simultaneously).
I am aware of a similar problem described here: Keyboard Animation Issues When Calling becomeFirstResponder within a Modal View Controller.
However, it seems like the behaviour of the search bar is sligtly differet then that of text field. I could not reproduce the steps described by that author to make the keyboard visible by calling becomeFirstResponder in viewDidLoad.
Regards,
Chris
I think I found your answer. When you add a search bar using the interface builder, you can do it two ways: "Search bar" and "Search bar and Search Display Controller".
I was using the second and was having the very same problem you described. I could only invoke the keyboard (using becomeFirstResponder) on "viewDidAppear". But if you do it adding just the search bar it works. Now I can call becomeFirstResponder on "viewDidLoad" and the keyboard appears together with the view itself.
I means a little more work, but really not much. You have to set your controller to be the delegate of the search bar. I added a list view for the results and made my controller become its delegate and its datasource.

Main Menu in different nibs

I have a Cocoa app with a ManMenu.xib, in which the main menu is stored and different other subviews in several xibs handling different tasks of my app. So how do I make my sub-xibs or rather their NSViewControllerController receive actions from the menu?
i have an idea but I don't know if it's the right way: I subclass NSViewController and add some actions like - (IBAction)undo or - (IBAction)redo use instances of it as the files owner. Further I add a pointer to my app delegate pointing at the actual view or rather its controller and the menu sends the action to that pointer.
Wise solution?
You should hook up your menu items to the First Responder. Their action messages will then travel along the responder chain until they reach something that responds to them.
You'll need to make sure your view controller is in the responder chain by setting it as the next responder of something else in the responder chain. I would suggest the window controller.
Speaking of the window controller, you probably should be handling undo there rather than in a view controller—it would be confusing for different views in the same window to have different undo lists. The only exception I can think of would be if the controlled views correspond to document tabs, like in Photoshop or Xcode.