Can't we use touchesBegan,touchesMoved,touchesEnded for a scrollview? - objective-c

Actually I'm having a scroll-view. In that I'm using 30-buttons. what's my requirement is I need to rearrange the buttons. Like, when i touched any button it should be selected with our touch. and where ever we move in the scroll-view it should move along with our touch. after i ended the touch, the buttons should be swapped. Can any one help me regarding this.........

You can do this behavior but there a lot of work. You need next:
1. Create UIControl subclass that will be your buttons.
2. Override all touches* methods there.
3. Implement there support of standard uicontrol behavior + moving behavior.
#pragma mark -
#pragma mark Touches delegate methods
- (void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event {
UITouch *touch = [touches anyObject];
[self performSelector:#selector(delayedTouchBeging:) withObject:touch afterDelay:0.15];
[super touchesBegan:touches withEvent:event];
}
- (void)touchesMoved:(NSSet *)touches withEvent:(UIEvent *)event {
UITouch *touch = [touches anyObject];
if ( dragging ) {
// move your view in new position here
} else {
[super touchesMoved:touches withEvent:event];
}
}
- (void)touchesEnded:(NSSet *)touches withEvent:(UIEvent *)event {
if ( dragging ) {
// Do other stuff if you need
} else {
[super touchesEnded:touches withEvent:event];
}
dragging = NO;
[NSObject cancelPreviousPerformRequestsWithTarget:self];
}
- (void)touchesCancelled:(NSSet *)touches withEvent:(UIEvent *)event {
[self touchesEnded:touches withEvent:event];
}
- (void) delayedTouchBeging:(UITouch*) touch {
dragging = YES;
[self cancelTrackingWithEvent:nil];
// Do here stuff about begin moving (animation, etc)
}

You may want to look into how they do the "Launcher" code as a part of the three20 code catalog that's available for free. They do exactly something like this (and mimicks the iPhone apps view where you can move apps around or delete them).
http://github.com/facebook/three20

Related

TouchesMoved with UIButton xcode 4.4 iOS 5.1.1

I know this question seems to be already asked and answered but I can't resolve my issue.
I want to move the objects of my view to follow horizontal slides of a finger (going to previous or next page)
For this, I use TouchesBegan, TouchesMoved and TouchesEnded methods.
It works fine when the touches began on the view background but does not work when touches began on uibuttons.
To solve the problem, I have subclassed the uibuttons which i want to allow the gesture and it was working fine when i was on xCode 3.x and iOS 4.x
Today I just installed OSX Moutain Lion and started to work on xCode 4.4 and iOS 5.1.1 (iPAD).
And here I am with the very same app that worked fine and not working anymore.
Here is my UIButton subclass:
// myUIButton.h
#import <UIKit/UIKit.h>
#interface myUIButton : UIButton
- (void) touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event;
- (void) touchesMoved:(NSSet *)touches withEvent:(UIEvent *)event;
- (void) touchesEnded:(NSSet *)touches withEvent:(UIEvent *)event;
#end
// myUIButton.m
#import "myUIButton.h"
#implementation myUIButton
- (void) touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event {
NSLog(#"uibutton touches began");
[self.nextResponder touchesBegan:touches withEvent:event];
}
- (void) touchesMoved:(NSSet *)touches withEvent:(UIEvent *)event {
NSLog(#"uibutton touches moved");
[self.nextResponder touchesMoved:touches withEvent:event];
}
- (void) touchesEnded:(NSSet *)touches withEvent:(UIEvent *)event {
NSLog(#"uibutton touches ended");
[self.nextResponder touchesEnded:touches withEvent:event];
}
#end
Here are the methods in my main class:
- (void) touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event {
NSLog(#"view touches began");
}
- (void) touchesMoved:(NSSet *)touches withEvent:(UIEvent *)event {
NSLog(#"view touches moved");
}
- (void) touchesEnded:(NSSet *)touches withEvent:(UIEvent *)event {
NSLog(#"view touches ended");
}
When my touch began on an UIButton I get every NSLog from the subclass but the main touchesMoved is executed only once. (self.nextResponder seems to works only once)
Did something changed that make it not works on xcode 4.4 or iOS 5.1.1 ?
When I first tried to do that (on xcode 3.x and iOS 4.x) i found the solution here:
Is there a way to pass touches through on the iPhone?
But it not works anymore...
Could you help me?
Thanks a lot.
EDIT : The problem is the same using [super touches...] instead or in more of [self.nextResponder touches...]
I have solved the problem a while ago and does not remember how but there are differences in my files so I'll write here the new version that works.
// myUIButton.h
#import <Foundation/Foundation.h>
#interface myUIButton : UIButton {
}
#end
.
// myUIButton.m
#import "myUIButton.h"
#implementation myUIButton
- (void) touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event {
NSLog(#"uibutton touches began");
[super touchesBegan:touches withEvent:event];
[self.nextResponder touchesBegan:touches withEvent:event];
}
- (void) touchesMoved:(NSSet *)touches withEvent:(UIEvent *)event {
NSLog(#"uibutton touches moved");
[super touchesMoved:touches withEvent:event];
[self.nextResponder touchesMoved:touches withEvent:event];
}
- (void) touchesEnded:(NSSet *)touches withEvent:(UIEvent *)event {
NSLog(#"uibutton touches ended");
[super touchesEnded:touches withEvent:event];
[self.nextResponder touchesEnded:touches withEvent:event];
}
#end
In each view I need this behavior, I place a Pan Gesture Recognizer in the file.xib that I link to the action panRecognizer: defined in the file.m
// file.m
#synthesize slide_origin_x;
-(IBAction)panRecognizer:(UIPanGestureRecognizer *)recognizer {
// Recording movement
if (recognizer.state == UIGestureRecognizerStateBegan) {
self.slide_origin_x = [NSNumber numberWithFloat:recognizer.view.center.x];
}
// Choose an action
else if (recognizer.state == UIGestureRecognizerStateEnded) {
int to_launch_action = 100; // Action choosen after 100 pixels
int touch_move = [self.slide_origin_x intValue] - recognizer.view.center.x;
if (touch_move > (0 + to_launch_action)) {
/*
* What happens if the view slide to the left (eg : go_to_next)
*/
} else if (touch_move < (0 - to_launch_action)) {
/*
* What happens if the view slide to the right (eg : go_to_previous)
*/
}
// The view goes back to normal
recognizer.view.center = CGPointMake([self.slide_origin_x intValue], recognizer.view.center.y);
}
// The view is moved
else {
CGPoint translation = [recognizer translationInView:self.view];
recognizer.view.center = CGPointMake(recognizer.view.center.x + translation.x,
recognizer.view.center.y);
[recognizer setTranslation:CGPointMake(0,0) inView:self.view];
}
}
FYI : This works also on XCode5 and SDK6.1

Detect anytouch inside subView (other than a button) touchesEnded

I have a subview, which also contains a UIButton.
Now, once I enter the touchesEnded, I want to execute some instructions only if the main subview was touched (and not the UIButton).
Is there a way to do this??
Thanks in advance.
ezbz
Use write your logic using these events ,by comparing the coordinates accordingly .
-(void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event{
}
-(void)touchesEnded:(NSSet *)touches withEvent:(UIEvent *)event{
UITouch *aTouch = [touches anyObject];
CGPoint point = [aTouch locationInView:self.view];
NSLog(#"X%f",point.x);
}
- (void)touchesMoved:(NSSet *)touches withEvent:(UIEvent *)event{
}

How to get the location of a touch in touchesBegan function

I tried the following code, but it doesn't work. How should it be modified?
- (void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event
{
location = [touches locationInView:self];
}
In the h files I have defined location like this:
CGPoint location;
The (NSSet *)touches will give you all the current touches on the screen. You will need to go on each touch on this set and get it's coordinate.
These touches are members of UITouch class. Have a look at the UITouch class reference.
for instance you would do:
- (void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event {
for (UITouch *touch in touches) {
CGPoint location = [touch locationInView:self];
// Do something with this location
// If you want to check if it was on a specific area of the screen for example
if (CGRectContainsPoint(myDefinedRect, location)) {
// The touch is inside the rect, do something with it
// ...
// If one touch inside the rect is enough, break the loop
break;
}
}
}
Cheers!

Using an Instance Variable in more than one method

How would I save an Instance so that I could use it in another method?
This is the code I have:
- (void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event
{
CGPoint touchedStart;
// find position of the touch
touchedStart = [[touches anyObject] locationInView:self];
}
- (void)touchesEnded:(NSSet *)touches withEvent:(UIEvent *)event
{
CGPoint touchedEnd;
// find position of the touch
touchedEnd = [[touches anyObject] locationInView:self];
}
I want to be able to use the touchedStart variable in the touchesEnded method. How would I do this?
Just make it a member variable in the class itself - then the other method will be able to access it.

Responding to touchesBegan in UIPickerView instead of UIView

I have a UIPickerView that gets faded out to 20% alpha when not in use. I want the user to be able to touch the picker and have it fade back in.
I can get it to work if I put a touchesBegan method on the main View, but this only works when the user touches the View. I tried sub-classing UIPickerView and having a touchesBegan in there, but it didn't work.
I'm guessing it's something to do with the Responder chain, but can't seem to work it out.
I've been searching for a solution to this problem for over a week. I'm answering you even if you're question is over a year old hoping this helps others.
Sorry if my language is not very technical, but I'm pretty new to Objective-C and iPhone development.
Subclassing UIpickerView is the right way to do it. But you've to override the - (UIView *)hitTest:(CGPoint)point withEvent:(UIEvent *)event method. This is the method called whenever you touch the screen and it returns the view that will react to the touch. In other words the view whose touchesBegan:withEvent: method will be called.
The UIPickerView has 9 subviews! In the UIPickerView class implementation - (UIView *)hitTest:(CGPoint)point withEvent:(UIEvent *)event won't return self (this means the touchesBegan:withEvent: you write in the subclass won't be called) but will return a subview, exactly the view at index 4 (an undocumented subclass called UIPickerTable).
The trick is to make the - (UIView *)hitTest:(CGPoint)point withEvent:(UIEvent *)event method to return self so you have control over the touchesBegan:withEvent:, touchesMoved:withEvent: and touchesEnded:withEvent: methods.
In these methods, in order to keep the standard functionalities of the UIPickerView, you MUST remember to call them again but on the UIPickerTable subview.
I hope this makes sense. I can't write code now, as soon as I'm at home I will edit this answer and add some code.
Here is some code that does what you want:
#interface TouchDetectionView : UIPickerView {
}
- (UIView *)getNextResponderView:(NSSet *)touches withEvent:(UIEvent *)event;
#end
#implementation TouchDetectionView
- (void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event
{
UIView * hitTestView = [self getNextResponderView:touches withEvent:event];
[hitTestView touchesBegan:touches withEvent:event];
}
- (void)touchesMoved:(NSSet *)touches withEvent:(UIEvent *)event
{
UIView * hitTestView = [self getNextResponderView:touches withEvent:event];
[hitTestView touchesMoved:touches withEvent:event];
}
- (void)touchesEnded:(NSSet *)touches withEvent:(UIEvent *)event
{
UIView * hitTestView = [self getNextResponderView:touches withEvent:event];
[hitTestView touchesEnded:touches withEvent:event];
}
- (void)touchesCancelled:(NSSet *)touches withEvent:(UIEvent *)event
{
UIView * hitTestView = [self getNextResponderView:touches withEvent:event];
[hitTestView touchesCancelled:touches withEvent:event];
}
- (UIView *)hitTest:(CGPoint)point withEvent:(UIEvent *)event
{
return self;
}
- (UIView *)getNextResponderView:(NSSet *)touches withEvent:(UIEvent *)event
{
UITouch * touch = [touches anyObject];
CGPoint point = [touch locationInView:self];
UIView * hitTestView = [super hitTest:point withEvent:event];
return ( hitTestView == self ) ? nil : hitTestView;
}
Both of the above answers were very helpful, but I have a UIPickerView nested within a UIScrollView. I'm also doing continual rendering elsewhere on-screen while the GUI is present. The problem is that the UIPickerView doesn't update fully when: a non-selected row is tapped, the picker is moved so that two rows straddle the selection area, or a row is dragged but the finger slides outside of the UIPickerView. Then it's not until the UIScrollView is moved that the picker instantly updates. This result is ugly.
The problem's cause: my continual rendering was keeping the UIPickerView's animation from getting the CPU cycles it needed to finish, hence to show the correct, current selection. My solution --which works-- was this: in the UIPickerView's touchesEnded:withEvent:, execute something to pause my rendering for a short while. Here's the code:
#import "SubUIPickerView.h"
#implementation SubUIPickerView
- (void) touchesBegan:(NSSet*)touches withEvent:(UIEvent*)event
{
[pickerTable touchesBegan:touches withEvent:event];
}
- (void) touchesMoved:(NSSet*)touches withEvent:(UIEvent*)event
{
[pickerTable touchesMoved:touches withEvent:event];
}
- (void) touchesEnded:(NSSet*)touches withEvent:(UIEvent*)event
{
[singleton set_secondsPauseRendering:0.5f]; // <-- my code to pause rendering
[pickerTable touchesEnded:touches withEvent:event];
}
- (void) touchesCancelled:(NSSet*)touches withEvent:(UIEvent*)event
{
[pickerTable touchesCancelled:touches withEvent:event];
}
- (UIView*) hitTest:(CGPoint)point withEvent:(UIEvent*)event
{
if (CGRectContainsPoint(self.bounds, point))
{
if (pickerTable == nil)
{
int nSubviews = self.subviews.count;
for (int i = 0; i < nSubviews; ++i)
{
UIView* view = (UIView*) [self.subviews objectAtIndex:i];
if ([view isKindOfClass:NSClassFromString(#"UIPickerTable")])
{
pickerTable = (UIPickerTable*) view;
break;
}
}
}
return self; // i.e., *WE* will respond to the hit and pass it to UIPickerTable, above.
}
return [super hitTest:point withEvent:event];
}
#end
and then the header, SubUIPickerView.h:
#class UIPickerTable;
#interface SubUIPickerView : UIPickerView
{
UIPickerTable* pickerTable;
}
#end
Like I said, this works. Rendering pauses for an additional 1/2 second (it already pauses when you slide the UIScrollView) allowing the UIPickerView animation to finish. Using NSClassFromString() means you're not using any undocumented APIs. Messing with the Responder chain was not necessary. Thanks to checcco and Tylerc230 for helping me come up with my own solution!
Set canCancelContentTouches and delaysContentTouches of parent view to NO, that worked for me
Couldn't find a recent, easier solution to this so I rigged something to solve my problem. Created an invisible button that stretches over the picker view. Connected that button with a "Touch Down" recognizer to my parent UIView. Now any functionality can be added, I happened to need a random selection timer to be invalidated when someone touches the picker view. Not elegant but it works.