Hi i'm trying to forward the touches I receive from an UIView which is in front of an UITableView. But doins so I can't make the table scroll anymore (see here).
So now I'm trying to make tha tableview scroll programmatically and I am looking at setContentOffset:animated: and scrollRectToVisible:animated: with having success only with the first.
Here is my code in a class that subclasses the UITableView:
- (void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event{
NSLog(#"BEGTB");
//previousXPan = 0.0;
UITouch * touch = [touches anyObject];
CGPoint tPoint = [touch locationInView:self];
previousYPan = tPoint.y;
[super touchesBegan:touches withEvent:event];
//
//[super touchesShouldBegin:touches withEvent:event inContentView:self];
//[[self nextResponder] touchesBegan:touches withEvent:event];
}
- (void)touchesMoved:(NSSet *)touches withEvent:(UIEvent *)event{
UITouch * touch = [touches anyObject];
CGPoint tPoint = [touch locationInView:self];
NSLog(#"MOVTB");
NSLog(#"BOUNDZ %f, %f, %f, %f", self.bounds.origin.x, self.bounds.origin.y, self.bounds.size.height, self.bounds.size.width);
NSLog(#"CONTENT SIZE %f, %f", self.contentSize.height, self.contentSize.width);
CGRect rc = [self bounds];
NSLog(#"%f %f", rc.size.height, rc.size.width);
CGRect newRect = CGRectMake(rc.origin.x, rc.origin.y + self.contentSize.height, self.contentSize.width, fabs(tPoint.y - previousYPan));
NSLog(#"BOND RECT %f, %f, %f, %f", rc.origin.x, rc.origin.y, rc.size.height, rc.size.width);
NSLog(#"NEW RECT %f, %f, %f, %f", newRect.origin.x, newRect.origin.y, newRect.size.height, newRect.size.width);
//[self scrollRectToVisible:newRect animated:YES];
NSLog(#"Content OFFSET %f",tPoint.y - previousYPan);
[self setContentOffset:CGPointMake(rc.origin.x,(tPoint.y - previousYPan) *2.0 )animated:YES];
previousYPan = tPoint.y;
//[super touchesMoved:touches withEvent:event];
//[[self nextResponder] touchesMoved:touches withEvent:event];
}
But the result is really laggy and buggy. Any better idea?
I almost found a suitable solution (it is not like a normal scrolling but...).
First I determine the mamimum and minimum amount of pixels in the content area of the table view on the Y axis ( the height of the contentSize when the view has been fully loaded).
The I check the movement done incrementally but not referencing the position of the touch in the table view! I have to check it on a top layered UIView that covers it, because when you alter the content offset of the table you change dynamically its dimensions and you can end you having an acceleration effect .
Here is some code:
- (void)touchesMoved:(NSSet *)touches withEvent:(UIEvent *)event{
UITouch * touch = [touches anyObject];
CGPoint tPoint = [touch locationInView:[viewController draggableAreaView]];
CGRect rc = [self bounds];
float totalOS = tPoint.y - previousYPan;
if ((contentOffSetY >= minY && contentOffSetY <= maxY) ||
(contentOffSetY < minY - 5 && totalOS < 0) ||
(contentOffSetY > maxY + 5 && totalOS > 0))
{
if (totalOS > 0)
totalOS += OFFSET;
else if (totalOS < 0)
totalOS -= OFFSET;
[self setContentOffset:CGPointMake(rc.origin.x,self.contentOffset.y + totalOS)animated:NO];
}
else /*if (contentOffSetY < minY - 5 && totalOS < 0) */
{
[self resetOffset];
}
previousYPan = tPoint.y;
}
- (void)resetOffset{
CGRect rc = [self bounds];
if(contentOffSetY < minY)
{
[self setContentOffset:CGPointMake(rc.origin.x, minY + 50)animated:YES];
}
else if (contentOffSetY > maxY)
{
[self setContentOffset:CGPointMake(rc.origin.x, maxY - 50)animated:YES];
}
}
Some more tips:
here draggableAreaView is the top UIView from which I get an "absolute" position
OFFSET is a constant defined to increment linearly the speed of the scroolling (without it the touchesMoved: , called many times, gives a relative movement of just one pixel each time)
,minY and maxY are the precalculated limit values (in order to compute them I calculated the height of all the cells in the table for max and the height of the visible content view for min)
I added some displacement to minY and maxY in order to have a more visible animation
Related
i want to set imagesize same with the content size of the scrollview after double tap to the imageview.
i use ktphotobrowser in my app and in its ktphotoview object(it is a uiscrollview object) i have the below code for double tap
- (void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event
{
UITouch *touch = [touches anyObject];
if ([touch view] == self) {
if ([touch tapCount] == 2) {
[NSObject cancelPreviousPerformRequestsWithTarget:self selector:#selector(toggleChromeDisplay) object:nil];
//[self zoomToRect:[self zoomRectForScale:[self maximumZoomScale] withCenter:[touch locationInView:self]] animated:YES];
//[self setContentSize:imageView_.bounds.size];
//[self zoomToLocation:[touch locationInView:self]];
CGPoint pointInView = [touch locationInView:self];
// Get a zoom scale that's zoomed in slightly, capped at the maximum zoom scale specified by the scroll view
CGFloat newZoomScale = self.zoomScale * 2.0;
NSLog(#"zoomscale %f",newZoomScale);
newZoomScale = MIN(newZoomScale, self.maximumZoomScale);
// Figure out the rect we want to zoom to, then zoom to it
CGSize scrollViewSize = self.bounds.size;
CGFloat w = scrollViewSize.width / newZoomScale;
CGFloat h = scrollViewSize.height / newZoomScale;
CGFloat x = pointInView.x - (w / 2.0f);
CGFloat y = pointInView.y - (h / 2.0f);
CGRect rectToZoomTo = CGRectMake(x, y, w, h);
[self zoomToRect:rectToZoomTo animated:YES];
}
}
}
scrollview's maximumzoomscale is set to 2.0
my image's original size is 1280x853 when i double tapped myimageview's bound die height will be 640 and the height of the content size of scrollview is will be 960 (uiscreen.size.height * maximumzoomscale)
but what i want is both image view 's height and height of the content size should be same so user can not scroll to outside of image
how and where can i make these calculations and setting in this view where can i set height and widths both of them?
thanks
maybe not the best solution but i solve my problem with the below code block:
- (void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event
{
UITouch *touch = [touches anyObject];
if ([touch view] == self) {
if ([touch tapCount] == 2) {
[NSObject cancelPreviousPerformRequestsWithTarget:self selector:#selector(toggleChromeDisplay) object:nil];
[self zoomToRect:[self zoomRectForScale:[self maximumZoomScale] withCenter:[touch locationInView:self]] animated:YES];
float abc=(self.contentSize.height-(imageView_.image.size.height*320/imageView_.image.size.width*self.maximumZoomScale))/2 *-1;
[self setContentInset:UIEdgeInsetsMake(abc, 0, abc, 0)];
}
}
}
structure
UIViewController
- UIScrollview
- UIButton
I'm going to make scrollview can receive button event. So whenever user do scrolling(dragging) on the button, the scrollview react as scrolling itself.
I made button down and moved handler with event forwarding like below
- (IBAction)buttonTouchedMove:(id)sender withEvent:(UIEvent *)event {
[[sender nextResponder] touchesMoved:[event allTouches] withEvent:event];
}
- (IBAction)buttonTouchedDown:(id)sender withEvent:(UIEvent *)event {
[[sender nextResponder] touchesBegan:[event allTouches] withEvent:event];
}
and to move scrollview as touches change, I made below codes
- (void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event {
UITouch *touch = [touches anyObject];
self.oldPoint = [touch locationInView:self.view];
}
- (void)touchesMoved:(NSSet *)touches withEvent:(UIEvent *)event {
CGPoint offset = self.scrollView1.contentOffset;
UITouch *touch = [touches anyObject];
self.newPoint = [touch locationInView:self.view];
int diffX = newPoint.x - oldPoint.x;
offset.x = offset.x - diffX;
[scrollView1 setContentOffset:offset animated:YES];
self.oldPoint = self.newPoint;
}
but, scrollview react strange.. not move enough as I touch moved.
// get the button width
CGFloat buttonWidth = self.theButton.frame.size.width;
// replace your button name here
// now get the view width
CGFloat viewWidth = self.view.frame.size.width;
// replace your view name here
// now get the multiplier which i said u as 10
CGFloat mult = viewWidth / buttonWidth;
// and now use it in your code
.
.
.
diffX = mult*(newPoint.x - oldPoint.x);
.
.
.
Final answer, Using UIView animation, I got what i want.
- (IBAction)buttonTouchedDown:(id)sender withEvent:(UIEvent *)event {
UITouch *touch = [[event allTouches] anyObject];
self.oldPoint = [touch locationInView:self.view];
self.velocity = 0;
isButtonTouchedDown = YES;
}
- (IBAction)buttonTouchedMove:(id)sender withEvent:(UIEvent *)event {
CGPoint offset = self.scrollView1.contentOffset;
UITouch *touch = [[event allTouches] anyObject];
self.newPoint = [touch locationInView:self.view];
int diffX = self.newPoint.x - self.oldPoint.x;
velocity = diffX;
offset.x = offset.x - diffX;
[self.scrollView1 setContentOffset:offset animated:NO];
self.oldPoint = self.newPoint;
}
There is a trick..I calculated velocity by differentiating moved distance in TouchedMove function.
In Button TouchUp event handler, I controlled Scrollview with velocity value and CurveEaseout animation to scroll more. By offering very short duration of animation and repeating it if there is no event(button touched down). It becomes very similar with scrollview's animation.
- (IBAction)buttonTouchedUp:(id)sender withEvent:(UIEvent *)event {
CGPoint offset = self.scrollView1.contentOffset;
amountX = (self.velocity)*(abs(self.velocity));
dX = amountX;
isButtonTouchedDown = NO;
if (offset.x - amountX < 0) {
offset.x = 0;
[scrollView1 setContentOffset:offset animated:YES];
}
else if (abs(dX) < 70) { // 70: content item size
offset.x = roundf(offset.x/70)*70;
[scrollView1 setContentOffset:offset animated:YES];
}
else {
[self endScrollAnimation];
}
- (void)startAnimation:(float)x moveAmount:(float)moveAmount
{
CGPoint offset = self.scrollView1.contentOffset;
[UIView beginAnimations:nil context:nil];
[UIView setAnimationDuration:duration];
offset.x = x < 0 ? offset.x + moveAmount : offset.x -moveAmount;
[UIView setAnimationCurve:UIViewAnimationCurveLinear];
[UIView setAnimationDelegate:self];
[UIView setAnimationDidStopSelector:#selector(endScrollAnimation)];
[scrollView1 setContentOffset:offset animated:NO];
[UIView commitAnimations];
}
- (void)endScrollAnimation
{
CGPoint offset = self.scrollView1.contentOffset;
float slowMoveAmount = abs(amountX) * 0.05;
float fastMoveAmount = abs(amountX) * 0.1;
if (isButtonTouchedDown) {
return;
}
else if (abs(dX) > abs(amountX)*0.35 && offset.x > 0) {
[self startAnimation:dX moveAmount:fastMoveAmount];
dX = dX < 0 ? dX + fastMoveAmount : dX -fastMoveAmount;
}
else if (abs(dX) > slowMoveAmount && offset.x > 0) {
[self startAnimation:dX moveAmount:slowMoveAmount];
dX = dX < 0 ? dX + slowMoveAmount : dX - slowMoveAmount;
}
else {
offset.x = roundf(offset.x/70)*70;
[scrollView1 setContentOffset:offset animated:YES];
}
}
I am trying to drag an image view. I have got little bit of success in doing so, but it is not behaving as i want. I wish that it should move only if touch inside the image and drag it.
But it is moving even if I am touching and dragging from anywhere on the screen.
I have written code like this:
-(void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event
{
//retrieve touch point
CGPoint pt= [[ touches anyObject] locationInView:[self.view.subviews objectAtIndex:0]];
startLocation = pt;
}
- (void) touchesMoved:(NSSet*)touches withEvent:(UIEvent*)event
{
CGPoint pt = [[touches anyObject] locationInView: [self.view.subviews objectAtIndex:0]];
CGRect frame = [[self.view.subviews objectAtIndex:0]frame];
frame.origin.x += pt.x - startLocation.x;
frame.origin.y += pt.y - startLocation.y;
[[self.view.subviews objectAtIndex:0] setFrame: frame];
}
The return value of locationInView method is point relative to the view frame. check it is or not in the view frame first.
-(void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event
{
CGRect targetFrame = [self.view.subviews objectAtIndex:0].frame;
//retrieve touch point
CGPoint pt= [[ touches anyObject] locationInView:[self.view.subviews objectAtIndex:0]];
//check if the point in the view frame
if (pt.x < 0 || pt.x > targetFrame.size.width || pt.y < 0 || pt.y > targetFrame.size.height)
{
isInTargetFrame = NO;
}
else
{
isInTargetFrame = YES;
startLocation = pt;
}
}
- (void) touchesMoved:(NSSet*)touches withEvent:(UIEvent*)event
{
if(!isInTargetFrame)
{
return;
}
//move your view here...
}
Try something like this:
-(void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event
{
//retrieve touch point
startLocation = [[ touches anyObject] locationInView:self.view];
// Now here check to make sure that start location is within the frame of
// your subview [self.view.subviews objectAtIndex:0]
// if it is you need to have a property like dragging = YES
// Then in touches ended you set dragging = NO
}
- (void) touchesMoved:(NSSet*)touches withEvent:(UIEvent*)event
{
CGPoint pt = [[touches anyObject] locationInView: [self.view.subviews objectAtIndex:0]];
CGRect frame = [[self.view.subviews objectAtIndex:0]frame];
frame.origin.x += pt.x - startLocation.x;
frame.origin.y += pt.y - startLocation.y;
[[self.view.subviews objectAtIndex:0] setFrame: frame];
This is my ccTouchesMoved method.
Whats wrong? I use cocos2d framework.
-(void) ccTouchesMoved:(NSSet *)touches withEvent:(UIEvent *)event {
CCNode *sprite = [self getChildByTag:kTagPlayer];
CCNode *sprite2 = [self getChildByTag:kTagEnemy];
CGPoint point;
//Собрать все касания.
NSSet *allTouches = [event allTouches];
for (UITouch *touch in allTouches)
{
point = [touch locationInView:[touch view]];
point = [[CCDirector sharedDirector] convertToGL:point];
if (point.y > 384)
{
if (point.x > 992)
sprite2.position = ccp(992, size.height - 100);
else if (point.x < 32)
sprite2.position = ccp(32, size.height - 100);
else
sprite2.position = ccp(point.x, size.height - 100);
}
else
{
if (point.x > 992)
sprite.position = ccp(992, 100);
else if (point.x < 32)
sprite.position = ccp(32, 100);
else
sprite.position = ccp(point.x, 100);
}
}
}
Have you enabled multiple touches in your glView? By default the glView is instantiated in the app delegate. The code is below.
[glView setMultipleTouchEnabled:YES];
In case you're developing a Retina display App, be aware that all coordinates are in points, not pixels. So even on a Retina display with 960x640 pixels the coordinates in points (your touch location) will be in the range 480x320.
If you want to use pixels, use the "InPixels" version of all coordinates, in this case:
sprite.positionInPixels = ccp(992, 100);
If that's not the problem you should add to your post what the expected outcome is and what happens instead. A little context goes a long way.
What does the debugger say is in allTouches? You could try getting all the touches for the view like this instead:
UITouch* touch = [touches anyObject];
NSSet* allTouches = [touches setByAddingObjectsFromSet:[event touchesForView:[touch view]]];
In touchesBegan:
CGPoint touch_point = [[touches anyObject] locationInView:self.view];
There are tens of UIImageView around, stored in a NSMutableArray images. I'd like to know is there a built-in function to check if a CGPoint (touch_point) is inside one of the images, e.g.:
for (UIImageView *image in images) {
// how to test if touch_point is tapped on a image?
}
Thanks
Follow up:
For unknown reason, pointInside never returns true. Here is the full code.
- (void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event
{
UITouch *touch = [touches anyObject];
touch_point = [touch locationInView:self.view];
for (UIImageView *image in piece_images) {
if ([image pointInside:touch_point withEvent:event]) {
image.hidden = YES;
} else {
image.hidden = NO;
}
NSLog(#"image %.0f %.0f touch %.0f %.0f", image.center.x, image.center.y, touch_point.x, touch_point.y);
}
}
although I can see the two points are sometimes identical in the NSLog output.
I also tried:
if ([image pointInside:touch_point withEvent:nil])
the result is the same. never returns a true.
To eliminate the chance of anything goes with with the images. I tried the following:
if (YES or [image pointInside:touch_point withEvent:event])
all images are hidden after the first click on screen.
EDIT 2:
Really weird. Even I hard coded this:
point.x = image.center.x;
point.y = image.center.y;
the code becomes:
- (void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event
{
UITouch *touch = [touches anyObject];
CGPoint point; // = [touch locationInView:self.view];
for (UIImageView *image in piece_images) {
point.x = image.center.x;
point.y = image.center.y;
if ([image pointInside:point withEvent:event]) {
image.hidden = YES;
NSLog(#"YES");
} else {
image.hidden = NO;
NSLog(#"NO");
}
NSLog(#"image %.0f %.0f touch %.0f %.0f", image.center.x, image.center.y, point.x, point.y);
}
}
pointInside always returns false ...
Sounds to me like the problem is you need to convert coordinates from your view's coordinates to the UIImageView's coordinates. You can use UIView's convertPoint method.
Try replacing
if ([image pointInside:touch_point withEvent:event]) {
with
if ([image pointInside: [self.view convertPoint:touch_point toView: image] withEvent:event]) {
(Note that you are allowed to pass nil instead of event.)
I guess this is a bit late for the questioner, but I hope it is of use to someone.
if ([image pointInside:touch_point withEvent:event]) {
// Point inside
}else {
// Point isn't inside
}
In this case event is taken from :
- (void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event;
- (void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event
{
UITouch *touch = [touches anyObject];
CGPoint point = [touch locationInView:self.view];
for (UIImageView *piece in piece_images) {
CGFloat x_min = piece.center.x - (piece.bounds.size.width / 2);
CGFloat x_max = x_min + piece.bounds.size.width;
CGFloat y_min = piece.center.y - (piece.bounds.size.height / 2);
CGFloat y_max = y_min + piece.bounds.size.height;
if (point.x > x_min && point.x < x_max && point.y > y_min && point.y < y_max ) {
piece.hidden = YES;
} else {
piece.hidden = NO;
}
}
}
too bad, I have do it myself...
it's OS 3.2, XCode 3.2.2, tried both on simulator and iPad