I'm trying to figure out why I get an error when I use locationOfTouch:inView. Eventually I created a new view with just the locationOfTouch call and I still get a SIGABRT whenever I touch the view.
Aside from import statements, here's all the code in my view:
#interface Dummy : UIView <UIGestureRecognizerDelegate> {
UIPanGestureRecognizer *repositionRecognizer;
}
#end
Here's the impl:
#implementation Dummy
- (id)initWithFrame:(CGRect)frame
{
self = [super initWithFrame:frame];
if (self) {
repositionRecognizer = [[UIPanGestureRecognizer alloc]
initWithTarget:self
action:#selector(reposition:)];
[repositionRecognizer setDelegate:self];
[self addGestureRecognizer:repositionRecognizer];
self.backgroundColor = [UIColor grayColor];
}
return self;
}
- (void)reposition:(UIGestureRecognizer *) gestureRecognizer {
[gestureRecognizer locationOfTouch:0 inView:self];
//[gestureRecognizer locationInView:self];
}
#end
If I use locationInView, it works okay. If I use locationOfTouch:inView, the program aborts as soon as the touch ends.
EDIT: On the console, with this class, no error messages are shown. The IDE points to main.m with a SIGABRT. Clicking on 'Continue' results in 'EXC_BAD_INSTRUCTION'. Screenshot available on http://imageshack.us/photo/my-images/849/consolel.png/
This crashes because it assumes that there is a touch zero. You need to confirm there is one first, like so:
- (void)reposition:(UIGestureRecognizer *) gestureRecognizer {
if (gestureRecognizer.numberOfTouches > 0){
CGPoint point = [gestureRecognizer locationOfTouch:0 inView:self];
NSLog(#"%#",NSStringFromCGPoint(point));
}
}
Think of the "locationOfTouch:" part as saying "touchAtIndex:", if the array of touches is empty(When you lift your finger or move it off the screen) then there is no touchAtIndex:0.
Related
Can anyone give me an example about Touch drag enter to drag from a button to another that triggering both button's event.
And how does it work?
For example, I want to drag from Do to Fa that event of Do, Re, Mi, Fa are triggered.
Here is my code:
- (void) setupVC {
soundBankPlayer = [[SoundBankPlayer alloc] init];
[soundBankPlayer setSoundBank:#"Piano"];
arrMusicalNotes = [NSArray arrayWithObjects:#"60", #"62", #"64", #"65", #"67", #"69", #"71", #"72", nil];
}
#pragma mark - Setup Musical Note
- (IBAction)btnMusicalNoteclick:(id)sender {
int numOfNote = [[arrMusicalNotes objectAtIndex:((UIButton*)sender).tag] intValue];
NSLog(#"%i", numOfNote);
[soundBankPlayer queueNote:numOfNote gain:1.0f];
[soundBankPlayer playQueuedNotes];
}
- (IBAction)btnDragOut:(id)sender {
NSLog(#"Out");
}
Oh I've seen that when i hold click out of Simulator, the method btnDragOut is triggered. And when i drag from out of Simulator to the button, the method of this button is triggered.
Now I want the method btnDragOut is triggered when i drag out of a button (finger is still in Simulator). Anyone know that?
You can add UIPanGestureRecognizer to your view of your UIViewController subclass via Storyboard or via code in viewDidLoad method:
UIPanGestureRecognizer *gestureRecognizer = [[UIPanGestureRecognizer alloc] initWithTarget:self action:#selector(handleDrag:)];
[self.view addGestureRecognizer:gestureRecognizer];
Then you can add property in implementation file of your UIViewController subclass of current UIButton being dragged:
#interface YourViewController ()
#property (weak, nonatomic) UIButton *currentButton;
#end
Now in action method you can detect UIControlEventTouchDragEnter and UIControlEventTouchDragExit events as follows:
- (void)handleDrag:(UIPanGestureRecognizer *)gestureRecognizer
{
CGPoint point = [gestureRecognizer locationInView:self.view];
UIView *draggedView = [self.view hitTest:point withEvent:nil];
if (gestureRecognizer.state == UIGestureRecognizerStateChanged) {
if ([draggedView isKindOfClass:[UIButton class]] && !self.currentButton) {
self.currentButton = (UIButton *)draggedView;
NSLog(#"Enter: %ld", (long)self.currentButton.tag);
// send enter event to your button
[self.currentButton sendActionsForControlEvents:UIControlEventTouchDragEnter];
}
if (self.currentButton && ![self.currentButton isEqual:draggedView]) {
NSLog(#"Out: %ld", (long)self.currentButton.tag);
// send exit event to your button
[self.currentButton sendActionsForControlEvents:UIControlEventTouchDragExit];
self.currentButton = nil;
}
} else if (gestureRecognizer.state == UIGestureRecognizerStateEnded) {
self.currentButton = nil;
}
}
I am new to Objective-C and Xcode and I am having difficulty in changing views. I understand you can use the UIScrollView to swipe between image views, but I would like to simply swipe right to change views, and swipe left to return. I have AppDelegate.h and AppDelegate.m which have not been changed. ViewController.h and ViewController.m are given below:
ViewController.h:
#import <UIKit/UIKit.h>
#interface ViewController : UIViewController
#end
ViewController.m:
#import "ViewController.h"
#import "ViewController2.h"
#interface ViewController ()
#end
#implementation ViewController
- (void)viewDidLoad
{
[super viewDidLoad];
//sets up gesture recognizer
UISwipeGestureRecognizer *swipeRightGesture=[[UISwipeGestureRecognizer alloc] initWithTarget:self action:#selector(handleSwipeGesture:)];
[self.view addGestureRecognizer:swipeRightGesture];
swipeRightGesture.direction=UISwipeGestureRecognizerDirectionRight;
}
- (void)didReceiveMemoryWarning
{
[super didReceiveMemoryWarning];
// Dispose of any resources that can be recreated.
}
- (IBAction)handleSwipeGesture:(UISwipeGestureRecognizer *)sender
{
NSUInteger touches = sender.numberOfTouches;
if (touches == 2)
{
if (sender.state == UIGestureRecognizerStateEnded)
{
ViewController2 *vc2 = [[ViewController2 alloc] initWithNibName:#"ViewController2" bundle:nil];
[self.navigationController pushViewController:vc2 animated:YES];
}
}
}
#end
I also have ViewController2.h and ViewController2.m which I have linked to the second view in storyboard along with a gesture that I have linked to the handleSwipeGesture method above.
- (void)viewDidLoad
{
[super viewDidLoad];
UISwipeGestureRecognizer *oneFingerSwipeLeft = [[UISwipeGestureRecognizer alloc]
initWithTarget:self
action:#selector(oneFingerSwipeLeft:)] ;
[oneFingerSwipeLeft setDirection:UISwipeGestureRecognizerDirectionLeft];
[[self view] addGestureRecognizer:oneFingerSwipeLeft];
}
- (void)oneFingerSwipeLeft:(UITapGestureRecognizer *)recognizer
{
NSLog(#"Swiped left");
// Add your navigation Code Here
}
U can use this if u want to use single finger swipe. IF u want to add two finger Swipe add your
NSUInteger touches = sender.numberOfTouches;
if (touches == 2)
{
if (sender.state == UIGestureRecognizerStateEnded)
{
}
}
above code in the (void)onefingerSwipeLeft method
- (IBAction)handleSwipeGesture:(UISwipeGestureRecognizer *)sender does not need to be an IBAction since it is not hooked up to an IBOutlet. It should return void instead.
Additionally, instead of checking the number of touches once the gesture has already been recognized, you can make two touches a requirement for it to be recognized at all, by using the line:
swipeRightGesture.numberOfTouchesRequired = 2;
Consequently, the method -(void)handleSwipeGesture no longer needs to take an argument, so you can remove the colon from the line where you declare the gesture recognizer.
The final code might look similar to this:
UISwipeGestureRecognizer *swipeRightGesture=[[UISwipeGestureRecognizer alloc] initWithTarget:self action:#selector(handleSwipeGesture)];
swipeRightGesture.direction=UISwipeGestureRecognizerDirectionRight;
swipeRightGesture.numberOfTouchesRequired = 2;
[self.view addGestureRecognizer:swipeRightGesture];
...
...
- (void)handleSwipeGesture {
ViewController2 *vc2 = [[ViewController2 alloc] initWithNibName:#"ViewController2" bundle:nil];
[self.navigationController pushViewController:vc2 animated:YES];
}
I have added 20 subviews to scrollview line by line as rows
yPos=0;
for (int i=0; i<24; i++) {
UIView *timeView=[[UIView alloc]initWithFrame:CGRectMake(71, yPos, 909, 60)];
timeView.userInteractionEnabled=TRUE;
timeView.exclusiveTouch=YES;
timeView.tag=i;
NSLog(#"sub vieww tag=:%d",timeView.tag);
timeView.backgroundColor=[UIColor whiteColor];
UILabel *lbltime=[[UILabel alloc]initWithFrame:CGRectMake(0, 0, 70, 60)];
lbltime.text=#"VIEW HERE";
lbltime.textColor=[UIColor grayColor];
// [timeView addSubview:lbltime];
[scrlView addSubview:timeView];
yPos=yPos+61;
}
Now when ever I tap on a subview I am not getting the tapped subview properties.
like coordinates. It is giving parent view coordinates
I enabled subview UserInteractionEnabled to Yes.
Can any one tell me how to get tapped subview coordinate and tag value?
DO NOT subclass from UIScrollView, that's exactly why there are gesture recognizers. Also, DO NOT add a separate gesture recognizer to each view.
Add one gesture recognizer to your scroll view, and when it's clicked use the x,y values of the touch to calculate which view was clicked.
You'll need to do a small calculation: (y value of the click + UIScrollView y offset) / 60.
60 is the height of each view. This should return the index of the clicked view.
EDIT:
Code example:
- (void)viewTapped:(UIGestureRecognizer*)recognizer
{
CGPoint coords = [recognizer locationInView:recognizer.view];
int clickedViewIndex = (self.offset.y + coords.y) / 60;
// now clickedViewIndex contains the index of the clicked view
}
UIView *v = recognizer.view;
int tagNum = [v tag];
Using the tagNum you can do your further operatins.
Or v is your object of tapped view.
UITapGestureRecognizer* tap = [[UITapGestureRecognizer alloc] initWithTarget:self action:#selector(tap:)];
tap.numberOfTapsRequired = 1;
[timeview addGestureRecognizer:tap];
Add this in for loop only.
Make a class extending UIScrollView :
For example :
.h file :
#protocol CustomScrollViewDelegate <NSObject>
#optional
// optional becuase we always don't want to interact with ScrollView
- (void) customScrollViewTouchesEnded :(NSSet *)touches withEvent:(UIEvent *)event;
- (void) customScrollViewDidScroll;
#end
#interface CustomScrollView : UIScrollView
#property (weak, nonatomic) id<CustomScrollViewDelegate> touchDelegate;
// delegate was giving error becuase name is already there in UIScrollView
#end
Code for .m file :
#import "CustomScrollView.h"
#implementation CustomScrollView
- (id)initWithFrame:(CGRect)frame
{
self = [super initWithFrame:frame];
if (self) {
// Initialization code
}
return self;
}
- (void) touchesEnded:(NSSet *)touches withEvent:(UIEvent *)event
{
NSLog(#"touchesEnded");
if (!self.dragging) {
//NSLog(#"touchesEnded in custom scroll view");
if (_touchDelegate != nil) {
if ([_touchDelegate respondsToSelector:#selector(customScrollViewTouchesEnded:withEvent:)]) {
[_touchDelegate customScrollViewTouchesEnded:touches withEvent:event];
}
}
}
else {
// it wouldn't be called becuase at the time of dragging touches responding stops.
[super touchesEnded:touches withEvent:event];
}
}
#end
Implementing this use subview of scroll view, it will work
for (UILabel *label in [customScrollView subviews]) { // change name of table here
if ([label isKindOfClass:[UILabel class]]) {
if (label.tag == savedtag) { // use your tag
// write the code as desired
}
}
}
I am creating an app with cards. In my mainViewController, I have this code:
CardView *cardView = [[CardView alloc] initWithFrame:CGRectMake(0, 0, CardWidth, CardHeight)];
cardView.card = [player.closedCards cardAtIndex:t];
[self.cardContainerView addSubview:cardView];
[cardView animateDealingToBottomPlayer:player withIndex:t withDelay:delay];
delay += 0.1f;
where CardView is a subclass of UIView. Each Card is a unique cardView and in CardView.m I do have:
#implementation CardView
{
UIImageView *_backImageView;
UIImageView *_frontImageView;
CGFloat _angle;
}
#synthesize card = _card;
- (id)initWithFrame:(CGRect)frame
{
if ((self = [super initWithFrame:frame]))
{
self.backgroundColor = [UIColor clearColor];
[self loadBack];
self.userInteractionEnabled=YES;
}
return self;
}
- (void)loadBack
{
if (_backImageView == nil)
{
_backImageView = [[UIImageView alloc] initWithFrame:self.bounds];
_backImageView.image = [UIImage imageNamed:#"Back"];
_backImageView.contentMode = UIViewContentModeScaleToFill;
[self addSubview:_backImageView];
}
}
and the implementations for other functions .
Since in order to win space, one card is placed on top of the others (half of teh card is visible and the rest is covered by the next card and so on), I want to identify touches on each card.
If I place that code in CardView:
-(void)touchesEnded:(NSSet *)touches withEvent:(UIEvent *)event
{
NSLog(#"Card is touched");
}
it is never called. If I place it in the GameView Controller it is called anywhere that I will touch, but I do not know how to identify which cardView is called. Can you give me a hint?
EDIT:
I decided to use gestures. Therefore in my mainViewController changed the code to this:
for (PlayerPosition p = startingPlayer.position; p < startingPlayer.position + 4; ++p)
{
CardView *cardView = [[CardView alloc] initWithFrame:CGRectMake(0, 0, CardWidth, CardHeight)];
cardView.card = [player.closedCards cardAtIndex:t];
cardView.userInteractionEnabled=YES;
[self.cardContainerView addSubview:cardView];
[cardView animateDealingToBottomPlayer:player withIndex:t withDelay:delay];
delay += 0.1f;
UITapGestureRecognizer *recognizer=[[UITapGestureRecognizer alloc]initWithTarget:self action:#selector(cardSelected:)];
[cardView addGestureRecognizer:recognizer];
}
but this is never called.
-(void)cardSelected:(UITapGestureRecognizer *)recognizer
{
NSLog(#"Card Selected with gestures");
}
Why is that?
EDIT 2:
I have also tried adding this:
self.cardContainerView.userInteractionEnabled=YES;
or changing this:
UITapGestureRecognizer *recognizer=[[UITapGestureRecognizer alloc]initWithTarget:self action:#selector(cardSelected:)];
to this:
UITapGestureRecognizer *recognizer=[[UITapGestureRecognizer alloc]initWithTarget:self .cardContainerView action:#selector(cardSelected:)];
but none of them worked.
If you instead add a tap gesture recogniser to each card view you can get a callback to a method you specify and the gesture is connected to the view so you can directly get a reference to it.
Your CardView (which I guess is a subclass of UIView?) has 2 subviews, which are image views:
UIImageView *_backImageView;
UIImageView *_frontImageView;
You may want to set userInteractionEnabled to YES on one or both of them (depending on when the card should be tappable and when the subviews are shown and hidden).
You can also act as the delegate of the gesture (if you need to, or just temporarily to debug that the gesture is getting triggered but blocked by something other gesture).
I am trying to detect double tap in a uiwebview. following is the code. when i debug, debugger comes to initwithcoder but never to touchesbegan nor to selector function. why so ?
I am putting a UIWebView in my xib file and setting identity inspector class to MyWebView
#import "MyWebView.h"
#implementation MyWebView
- (void) initTap {
UITapGestureRecognizer *singleFingerDoubleTap = [[UITapGestureRecognizer alloc] initWithTarget:self action:#selector(handleSingleFingerDoubleTap:)];
singleFingerDoubleTap.numberOfTouchesRequired = 1;
singleFingerDoubleTap.numberOfTapsRequired = 2;
[self addGestureRecognizer:singleFingerDoubleTap];
[singleFingerDoubleTap release];
}
- (id)initWithCoder:(NSCoder*)coder
{
if (self = [super initWithCoder:coder]) {
// Initialization code.
[self initTap];
}
return self;
}
- (id)initWithFrame:(CGRect)frame {
self = [super initWithFrame:frame];
if (self) {
// Initialization code.
[self initTap];
}
return self;
}
-(void)handleSingleFingerDoubleTap:(id)sender {
NSLog(#"Doulble click detected !");
}
- (void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event {
UITouch *touch = [[event allTouches] anyObject];
if (touch.tapCount == 2) {
//put you zooming action here
NSLog(#"Doulble click detected !");
}
}
- (void)dealloc {
[super dealloc];
}
#end
You just need to add delegate to the recognizer
singleFingerDoubleTap.delegate = self;
Then implement the following method to return YES.
#pragma mark Gesture recognizer delegate
- (BOOL)gestureRecognizer:(UIGestureRecognizer *)gestureRecognizer shouldRecognizeSimultaneouslyWithGestureRecognizer:(UIGestureRecognizer *)otherGestureRecognizer
{
return YES;
}
Don't forget to add conform declaration to your class file.
Like this
#interface ORWebViewController : UIViewController<UIGestureRecognizerDelegate>
In case, you want to override the default zooming action on double tap, this will guide you.
UPDATE:
Please do not consider above mentioned reference as the author has moved it.
You can find the references from Disabling default zoom effect.