How to create various buttons which get called on simple touch - iphone

I have to make an app in which there are around 20 buttons, if I switch from one button to another button without removing the touch(i.e. by dragging the touch) I want to call the function when I entered in that button's frame region.
For example:when I drag touch from button's tag 10 to button's tag 11 then it should call button's tag 11 selector method.

I think you can not do it with UIButtons. But i have one suggestion for this may be it help you. Add Images instead of Buttons set those userIntractionEnable:NO then in touchesMoved method you can get call your #selector methods by comparing with X,Y co-ordinates.
I wrote a small code for this -
//I added your example image on my self.view with a view
//of 320x140 and set it userIntractionEnabled:NO
//Now in touchMoved: method did this..
-(void)touchesMoved:(NSSet *)touches withEvent:(UIEvent *)event
{
UITouch *touch = [[event allTouches] anyObject];
CGPoint pt = [touch locationInView:self.view];
//checking the touch is in self.view or not
if([touch view] == self.view)
{
//This is like a 2D array, So you have to follow row and column pattern.
if((pt.y>=0.0 && pt.y<=70.0))//For First Row
{
//These 7 are FirstRow Columns
if(pt.x>=0.0 && pt.x<=45.0){
NSLog(#"Method - 10");
}
if(pt.x>=46.0 && pt.x<=90.0){
NSLog(#"Method - 11");
}
if(pt.x>=91.0 && pt.x<=135.0){
NSLog(#"Method - 12");
}
if(pt.x>=136.0 && pt.x<=180.0){
NSLog(#"Method - 13");
}
if(pt.x>=181.0 && pt.x<=225.0){
NSLog(#"Method - 14");
}
if(pt.x>=226.0 && pt.x<=270.0){
NSLog(#"Method - 15");
}
if(pt.x>=271.0 && pt.x<=315.0){
NSLog(#"Method - 16");
}
}
//Row change this is for Second Row
if((pt.y>=71.0 && pt.y<=140.0))
{
//These 7 are SecondRow Columns
if(pt.x>=0.0 && pt.x<=45.0){
NSLog(#"Method - 17");
}
if(pt.x>=46.0 && pt.x<=90.0){
NSLog(#"Method - 18");
}
if(pt.x>=91.0 && pt.x<=135.0){
NSLog(#"Method - 19");
}
if(pt.x>=136.0 && pt.x<=180.0){
NSLog(#"Method - 20");
}
if(pt.x>=181.0 && pt.x<=225.0){
NSLog(#"Method - 21");
}
if(pt.x>=226.0 && pt.x<=270.0){
NSLog(#"Method - 22");
}
if(pt.x>=271.0 && pt.x<=315.0){
NSLog(#"Method - 23");
}
}
}
}
Hope this will help you !!!! :-)

You should register event with below way.
[button addTarget:self action:#selector(aMethod:) forControlEvents:UIControlEventTouchDragEnter];

Related

start click in UIButton, but use touchesEnded in main view

I have, for example, a UIButton that I placed in IB.
This UIButton has two functions depending on whether it is just touched up inside or dragged outside - it would be nice to at least keep it functioning for touch up inside.
I want to click on my button and 'drag outside' but use -touchesEnded over a rectangle when I finally release my click.
I know that one option is to ditch the UIButton and use -touchesBegan over a mask, but that is a lot more effort and requires a nice big rewrite (considering the 96 buttons).
So to rephrase slightly - I've clicked on a button and held down the mouse, while dragging the cursor (/finger) over to a different point in my view and I want my view to recognise a -touchesEnded when I finally let go. The problem is that the initial click is tied up in the button so -touchesEnded in my main view doesn't happen...
Am i trying to do this the wrong way? Also, am I being clear?
EDIT: #Heckman has phrased the question well below
If I understand your question correctly, you are touching one button, that is contained within some UIView, and want to be able to drag your finger out, and recognize the point in the buttons superview.
Sounds to me like you should detect the button press from the button, then make a call to the [UIButton superview], to tell the superview about the button pressing that started, and then handle the touch end within the view.
To notify the superview of the button that was pressed call something like (from your UIButton):
-(void) touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event {
[self.superview buttonPressed:self]
}
-(void) touchesEnded:(NSSet *)touches withEvent:(UIEvent *)event {
[self.superview touchesEnded:touches withEvent:event];
}
In your superview code (if you have a property buttonPressed):
-(void) buttonPressed:(UIButton *) button {
_buttonPressed = button;
}
-(void) touchesEnded:(NSSet *)touches withEvent:(UIEvent *)event {
if(self.buttonPressed) {
//Process the release from your drag here.
for(UITouch * touch in touches) {
//Use either of these to get the release position that you care about.
CGPoint releaseLocationInCurrentView = [touch locationInView:self];
CGPoint releaseLocationInButtonView = [touch locationInView:[self.buttonPressed]];
}
}
}
You might be able to use a UIPanGestureRecognizer on your UIView.
Here is some example code for how to implement gesture recognizers.
If you are looking for something like dragable items same as iphone homescreen icons then you can have a look to TTLauncherView sample from Three20 framework. http://three20.info/showcase/launcher
So, here's what I ended up doing, and it works quite nicely:
my UIButton on touchdragexit is assigned to an IBAction
-(IBAction)dragPoint:(id)sender {
//Do Something Useful in here - I assign the value to an Array that I can recall when I touch up inside one of 16 buttons
//Now pass the button touch on as a touch up outside
[myButton addTarget:self action:#selector(dragBegan:withEvent:) forControlEvents: UIControlEventTouchUpOutside];
}
so now I get my dragBegan like this:
- (void)dragBegan:(UIControl *)c withEvent:ev {
UITouch *touch = [[ev allTouches] anyObject];
CGPoint touchPoint = [touch locationInView:self.view]; // Get location of the moment I release the touch (touch up outside)
//now some long convoluted code to give me the location of a row of 16 buttons, of which I would like to know which one I touched up inside
UIButton *posButton;
int pos;
if(628.00<(touchPoint.y) && (touchPoint.y)<664.00){
if ((39.00<touchPoint.x && touchPoint.x<91.00) || (92.00<touchPoint.x && touchPoint.x<144.00) || (145.00<touchPoint.x && touchPoint.x<197.00) || (198.00<touchPoint.x && touchPoint.x<250.00)||(264.00<(touchPoint.x) && touchPoint.x<314.00)||(317.00<(touchPoint.x) && touchPoint.x<367.00)||(370.00<(touchPoint.x) && touchPoint.x<420.00)||(423.00<(touchPoint.x) && touchPoint.x<473.00)||(488.00<(touchPoint.x) && touchPoint.x<538.00) || (541.00<(touchPoint.x) && touchPoint.x<591.00) || (594.00<(touchPoint.x) && touchPoint.x<644.00) || (647.00<(touchPoint.x) && touchPoint.x<697.00)||(712.00<(touchPoint.x) && touchPoint.x<762.00)||(765.00<(touchPoint.x) && touchPoint.x<815.00)||(818.00<(touchPoint.x) && touchPoint.x<868.00)||(871.00<(touchPoint.x) && touchPoint.x<921.00)){
if(39.00<(touchPoint.x) && touchPoint.x<91.00){
posButton = SeqA;
pos=0;
}
else if(92.00<(touchPoint.x) && touchPoint.x<144.00){
posButton = SeqB;
pos=1;
}
else if(145.00<(touchPoint.x) && touchPoint.x<197.00){
posButton = SeqC;
pos=2;
}
else if(198.00<(touchPoint.x) && touchPoint.x<250.00){
posButton = SeqD;
pos=3;
}
else if(264.00<(touchPoint.x) && touchPoint.x<314.00){
posButton = SeqE;
pos=4;
}
else if(317.00<(touchPoint.x) && touchPoint.x<367.00){
posButton = SeqF;
pos=5;
}
else if(370.00<(touchPoint.x) && touchPoint.x<420.00){
posButton = SeqG;
pos=6;
}
else if(423.00<(touchPoint.x) && touchPoint.x<473.00){
posButton = SeqH;
pos=7;
}
else if(488.00<(touchPoint.x) && touchPoint.x<538.00){
posButton = SeqI;
pos=8;
}
else if(541.00<(touchPoint.x) && touchPoint.x<591.00){
posButton = SeqJ;
pos=9;
}
else if(594.00<(touchPoint.x) && touchPoint.x<644.00){
posButton = SeqK;
pos=10;
}
else if(647.00<(touchPoint.x) && touchPoint.x<697.00){
posButton = SeqL;
pos=11;
}
else if(712.00<(touchPoint.x) && touchPoint.x<762.00){
posButton = SeqM;
pos=12;
}
else if(765.00<(touchPoint.x) && touchPoint.x<815.00){
posButton = SeqN;
pos=13;
}
else if(818.00<(touchPoint.x) && touchPoint.x<868.00){
posButton = SeqO;
pos=14;
}
else if(871.00<(touchPoint.x) && touchPoint.x<921.00){
posButton = SeqP;
pos=15;
}
Now do something depending on the button you touched up inside
Other things I did - I created a mask of the original UIButton as a UIImageView that I made unhidden on a touchdragoutside event (and rebidden on the above touchupinside) so that I have a button I can drag

Clicking on a TTStyledTextLabel selects the UITableViewCell

I have a TTStyledTextLabel inside a UITableViewCell. Clicking on the cell navigates to a new view controller, so I can't disable selection, but when I click the TTStyledTextLabel, the UITableViewCell is also selected. Any thoughts on clicking the TTStyledTextLabel without also selecting the table view cell?
Just subclass TTStyledTextLabel and override the following two methods:
(void)touchesBegan:(NSSet*)touches withEvent:(UIEvent*)event
(void)touchesEnded:(NSSet*)touches withEvent:(UIEvent*)event
just like this:
- (void)touchesBegan:(NSSet*)touches withEvent:(UIEvent*)event {
UITouch* touch = [touches anyObject];
CGPoint point = [touch locationInView:self];
point.x -= _contentInset.left;
point.y -= _contentInset.top;
TTStyledBoxFrame* frame = [_text hitTest:point];
if (frame) {
[self setHighlightedFrame:frame];
}
}
- (void)touchesEnded:(NSSet*)touches withEvent:(UIEvent*)event {
TTTableView* tableView = (TTTableView*)[self ancestorOrSelfWithClass:[TTTableView class]];
if (!tableView) {
if (_highlightedNode) {
// This is a dirty hack to decouple the UI from Style. TTOpenURL was originally within
// the node implementation. One potential fix would be to provide some protocol for these
// nodes to converse with.
if ([_highlightedNode isKindOfClass:[TTStyledLinkNode class]]) {
TTOpenURL([(TTStyledLinkNode*)_highlightedNode URL]);
} else if ([_highlightedNode isKindOfClass:[TTStyledButtonNode class]]) {
TTOpenURL([(TTStyledButtonNode*)_highlightedNode URL]);
} else {
[_highlightedNode performDefaultAction];
}
[self setHighlightedFrame:nil];
}
}
}

iOS Loses Touches?

I can't find anything to explain lost UITouch events. If you smash your full hand on the screen enough times, the number of touchesBegan will be different than the number of touchesEnded! I think the only way to actually know about these orphaned touches will be to reference them myself and keep track of how long they haven't moved.
Sample code:
int touchesStarted = 0;
int touchesFinished = 0;
- (void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event
{
touchesStarted += touches.count;
}
- (void)touchesEnded:(NSSet *)touches withEvent:(UIEvent *)event
{
touchesFinished += touches.count;
NSLog(#"%d / %d", touchesStarted, touchesFinished);
}
- (void)touchesCancelled:(NSSet *)touches withEvent:(UIEvent *)event
{
[self touchesEnded:touches withEvent:event];
}
Don't forget about touchesCancelled: UIResponder reference
Editing in response to the poster's update:
Each touch object provides what phase it is in:
typedef enum {
UITouchPhaseBegan,
UITouchPhaseMoved,
UITouchPhaseStationary,
UITouchPhaseEnded,
UITouchPhaseCancelled,
} UITouchPhase;
I believe that if a touch starts and ends in the same touch event set, -touchesBegan:withEvent: will be called but will contain touches which have ended or cancelled.
You should change your counting code, then, to look like this:
int touchesStarted = 0;
int touchesFinished = 0;
- (void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event
{
[self customTouchHandler:touches];
}
- (void)touchesEnded:(NSSet *)touches withEvent:(UIEvent *)event
{
[self customTouchHandler:touches];
}
- (void)touchesCancelled:(NSSet *)touches withEvent:(UIEvent *)event
{
[self customTouchHandler:touches];
}
- (void)customTouchHandler:(NSSet *)touches
{
for(UITouch* touch in touches){
if(touch.phase == UITouchPhaseBegan)
touchesStarted++;
if(touch.phase == UITouchPhaseEnded || touch.phase == UITouchPhaseCancelled)
touchesFinished++;
}
NSLog(#"%d / %d", touchesStarted, touchesFinished);
}
Every single touch event will go through both phases of started and finished/cancelled, and so your counts should match up as soon as your fingers are off the screen.
One thing to remember... You may receive some touches that have multiple taps. Don't forget to take tapCount into account.
However, if you still have the problem, you can consider all touches from the event, though it presents some other management issues...
HACK ALERT
I have coded the following HACK to work around this. Sometimes the touchesEnded does not get called, BUT, the touches show up as part of all the touches in the event.
Note, that you can now process the same "canceled" or "ended" touch multiple times. If that is a problem, you have to keep your own state of "pending" touches, and remove them when done.
Yeah, it all is pretty bad, but I don't know how to overcome this issue without a similar hack. The basic solution is to look at all the touches in each event, and process them based on their phase, calling the appropriate ended/canceled when they are seen.
- (void) touchesEndedOrCancelled:(NSSet *)touches
{
__block NSMutableSet *ended = nil;
__block NSMutableSet *canceled = nil;
[touches enumerateObjectsUsingBlock:^(UITouch *touch, BOOL *stop) {
if (touch.phase == UITouchPhaseEnded) {
if (!ended) ended = [NSSet setWithObject:touch];
else [ended addObject:touch];
} else if (touch.phase == UITouchPhaseCancelled) {
if (!canceled) canceled = [NSSet setWithObject:touch];
else [canceled addObject:touch];
}
}];
if (ended) [self touchesEnded:ended withEvent:nil];
if (canceled) [self touchesCancelled:canceled withEvent:nil];
}
Then, call it at the end of touchesBegan and touchesMoved...
[self touchesEndedOrCancelled:event.allTouches];
For this to work, touchesEnded/Canceled needs to not choke on a nil event. Also, the "other" needs to be handled. In touchesEnded...
[self touchesCancelled:[event.allTouches objectsPassingTest:^BOOL(UITouch *touch, BOOL *stop) {
return touch.phase == UITouchPhaseCancelled;
}] withEvent:nil];
and in touchesCanceled...
[self touchesEnded:[event.allTouches objectsPassingTest:^BOOL(UITouch *touch, BOOL *stop) {
return touch.phase == UITouchPhaseEnded;
}] withEvent:nil];
You may need to provide more detail but....
If you run your finger off the edge of the screen this event will show touch started and touch moved but no end as it didn't actually end (lift finger). This could be your problem, that the event didn't happen. Plus there is a limit to the amount of touches in multi touch if you press say 10 times its then up to the system to determine what events are really and what are false, it seems like you are using the hardware incorrectly.

Detecting number of touches

guys, I want to set actions to touches. If people make single touch - one action, else - different.
I've written this code in my touchesBegan method:
UITouch *touch = [event.allTouches anyObject];
BOOL tappedTwice = NO;
if ([touch tapCount] == 2) {
tappedTwice = YES;
NSLog(#"double touch");
}
else if ([touch tapCount] == 1 && !tappedTwice) {
NSLog(#"single touch");
}
But it's detecting single touch, and after it double, but it's incorrect in my situation. Have you any ideas?
Check this link. Just Configure number of tap required by setting
[tapGestureRecognizer setNumberOfTapsRequired:2];
and then handle this method
- (void)handleTap:(UITapGestureRecognizer *)sender {
if (sender.state == UIGestureRecognizerStateEnded) {
// handling code
}
}

How to call a method only once during -(void)touchesMoved?

I am using - (void) touchesMoved to do stuff when ever I enter a specific frame, in this case the area of a button.
My problem is, I only want it to do stuff when I enter the frame - not when I am moving my finger inside the frame.
Does anyone know how I can call my methods only once while I am inside the frame, and still allow me to call it once again if I re-enter it in the same touchMove.
Thank you.
-(void)touchesMoved:(NSSet *)touches withEvent:(UIEvent *)event
{
UITouch *touch = [[event touchesForView:self.view] anyObject];
CGPoint location = [touch locationInView:touch.view];
if(CGRectContainsPoint(p1.frame, location))
{
//I only want the below to me called
// once while I am inside this frame
[self pP01];
[p1 setHighlighted:YES];
}else {
[p1 setHighlighted:NO];
}
}
You can use some attribute to check if the code was already called when you were entering specific area. It looks like highlighted state of p1 object (not sure what it is) may be appropriate for that:
if(CGRectContainsPoint(p1.frame, location))
{
if (!p1.isHighlighted){ // We entered the area but have not run highlighting code yet
//I only want the below to me called
// once while I am inside this frame
[self pP01];
[p1 setHighlighted:YES];
}
}else { // We left the area - so we'll call highlighting code when we enter next time
[p1 setHighlighted:NO];
}
Simply add a BOOL that you check in touchesMoved and reset in touchesEnded
if( CGRectContainsPoint([p1 frame],[touch locationInView:self.view])) {
NSLog (#"Touch Moved over p1");
if (!p14.isHighlighted) {
[self action: p1];
p1.highlighted = YES;
}
}else {
p1.highlighted = NO;
}
try using a UIButton and use the 'touch drag enter' connection in Interface Builder.