TouchMoved and wrong screen range? or Bug in iOS? - iphone

I found interesting bug in iOS, but trying to belive that i'm wrong. You have to do 2 things:
1) Create single-view template for iOS
2) Write small function in ViewController.m :
- (void)touchesMoved:(NSSet *)touches withEvent:(UIEvent *)event
{
UITouch* touch = [touches anyObject];
CGPoint point = [touch locationInView:[touch view]];
NSLog(#"%#", NSStringFromCGPoint(point));
}// so u can detect points of your touch
So if u try to move finger on display from screen's top to bottom (portrait mode) - you get points in range [-5.5 .. 469]... i can't explain this, it happens only on device, in simulator it works fine.
SOME DEBUG INFO:
with status bar and NO wantsFullScreenLayout range is: [-25.5 .. 449]
with status bar and YES wantsFullScreenLayout range is: [-5.5 .. 469]
without status bar and NO/YES FullScreenLayout the range is: [-5.5 .. 469]
with status bar and NO wantsFullScreenLayout view.frame is (0, 20, 320, 460) and view.bounds is (0, 0, 320, 460)
with status bar and YES wantsFullScreenLayout view.frame is (0, 0, 320, 480) and view.bounds is (0, 0, 320, 480)
without status bar and NO/YES FullScreenLayout view.frame is (0, 0, 320, 480) and view.bounds is too (0, 0, 320, 480)
Please, help to explain this stuff, it happens only on devices...

Because the status bar is out of limits of the view. And you gets negative values when you touch on status bar.
Try this:
- (void)touchesMoved:(NSSet *)touches withEvent:(UIEvent *)event
{
UITouch* touch = [touches anyObject];
CGPoint point = [touch locationInView:[touch window]];
NSLog(#"%#", NSStringFromCGPoint(point));
}

I found this from one of Apple's tutorials on a finger-painting program:
- (void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event
{
CGRect bounds = [self bounds];
UITouch* touch = [[event touchesForView:self] anyObject];
firstTouch = YES;
// Convert touch point from UIView referential to OpenGL one (upside-down flip)
location = [touch locationInView:self];
location.y = bounds.size.height - location.y;
}
Looks like you need to convert the touch to OpenGL coordinates to get the result you are expecting. Hope this helps.

The view of root viewController always acts like portrait mode. You should insert a new view inside of the root one. And this new view will acts correctly, will give right size and coordinates according to Apple says.
for example ;
UIView *v = self;
while ([[v superview] superview] != NULL) {
v = [v superview];
}
UITouch *touch = [touches anyObject];
CGPoint touchPoint = [touch locationInView:v];
touchPoint will be the correct one.

Related

Touch to hide tool bar

I want to hide and show tool bar when touch in The area CGRectMake(130, 0, 60, 480)
without using UIGestureRecognizer because its effect my other views
if touch once in the area , tool bar should hide and if tool bar is hidden , should show the tool bar
i have tried this
-(void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event
{
if (mainToolbar.hidden == YES) {
mainToolbar.hidden=NO;
}
else if(mainToolbar.hidden == NO){
[mainToolbar setHidden:YES];
}
}
but its hide tool bar when touch in to tool bar only
thanks......
Write your code in the view where you added tool bar. You will get location of touch using this code:
UITouch *touch = [[event allTouches] anyObject];
CGPoint location = [touch locationInView: touch.view];
And then check if touch is inside your rect using CGRectContainsPoint: function.
Another approach is simply put an button on the required area. Hope this helps
The method gives you an NSSet of all touches on the screen. You can use this to customize your behavior for touches. For example:
//This will change the state of whether mainToolbar is hidden or not. In the case of multiple touches, it will change the property if any touch is in the CGRect area.
-(void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event {
CGRect testRect = CGRectMake(130, 0, 60, 480);
for (UITouch *touch in touches) {
if (CGRectContainsPoint(testRect, [touch locationInView:self.view])) {
mainToolbar.hidden = !mainToolbar.hidden;
}
}
}
Or if you only want the action to happen if one touch is made and ignore it if the user is touching with multiple fingers, you would check for that:
-(void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event {
CGRect testRect = CGRectMake(130, 0, 60, 480);
if ([touches count] == 1) {
//If there is only one touch, we check for that. Otherwise, we ignore it.
UITouch *touch = [touches anyObject];
if (CGRectContainsPoint(testRect, [touch locationInView:self.view])) {
mainToolbar.hidden = !mainToolbar.hidden;
}
}
}
You can do some really cool things with just the four functions touchesBegan: touchesMoved: touchesEnded: and touchesCanceled:.

touchesMoved reaching out of my view bounds

I have subclassed UIView and there initially my view will be in a default color and i need to fill some different color on touch (from x axis = 0 to user touched point),here the problem is touchesMoved even if i drag out of my self view bounds it is getting those points,how to restrict it to only for my self view bounds.
I googled & tried below snippets but of no luck
if([self pointInside:point withEvent:nil]){
[self fillColor];
}
My touchesMoved method is as below,
- (void)touchesMoved:(NSSet *)touches withEvent:(UIEvent *)event
{
UITouch *touch = [touches anyObject];
CGPoint point = [touch locationInView:self];
endPoint = point;
NSLog(#"moved x: %f,y: %f",point.x,point.y);
if(CGRectContainsPoint([self frame], endPoint)){ // this also not working
[self fillColor];
}
}
Any help is appreciated in advance.
just set tag in viewDidLoad: method and use bellow logic..
fillColorView.tag = 111;
and use bellow logic in touchesMoved: method like bellow..
- (void)touchesMoved:(NSSet *)touches withEvent:(UIEvent *)event
{
UITouch *tap = [touches anyObject];
CGPoint pointToMove = [tap locationInView:fillColorView];
if([tap.view isKindOfClass:[UIView class]])
{
UIView *tempView=(UIView *) tap.view;
if (tempView.tag == 111){
[self fillColor];
}
}
}
hope this help you...
In your touchesMoved method, CGPoint point = [touch locationInView:self]; replcae self by the view in which you wants the touch to be worked.
self will get the complete view, you should pass your drawingView at there, so that it will detetc touch only on that view.

Values for touch locations fall out of view's bounds

I am trying to record the locations of the touches. Below is my code. As far as I understand, a touch at the very far upper left corner would give me a location of (0,0), and the very far lower right corner would be (768, 1024) supposing I'm holding the iPad in portrait. However, I'm getting values like (-6, -18) for the upper left corner and (761,1003) for the lower right. It looks like the coordinates are shifted somehow. A trace of self.bounds does give me {{0,0}, {768, 1024}}. Can someone explain this to me? I would like to get x and y value that are between the bounds {{0,0}, {768, 1024}}. Thank you very much in advance.
- (void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event
{
CGRect bounds = [self bounds];
NSLog(#"frame: %#", NSStringFromCGRect(bounds)); // this value was traced as frame: {{0, 0}, {768, 1024}}
UITouch* touch = [[event touchesForView:self] anyObject];
location = [touch locationInView:self];
NSLog(#"Location: %#", NSStringFromCGPoint(location));
}
- (void)touchesMoved:(NSSet *)touches withEvent:(UIEvent *)event
{
CGRect bounds = [self bounds];
UITouch* touch = [[event touchesForView:self] anyObject];
location = [touch locationInView:self];
NSLog(#"Location: %#", NSStringFromCGPoint(location));
}
- (void)touchesEnded:(NSSet *)touches withEvent:(UIEvent *)event
{
CGRect bounds = [self bounds];
UITouch* touch = [[event touchesForView:self] anyObject];
location = [touch locationInView:self];
NSLog(#"Location: %#", NSStringFromCGPoint(location));
}
Since I cannot comment yet, I have to post an answer. Have you tried setting the background color of the view to a different color so that you can see if its in the top left corner for sure?
Here's what I have for the view:
#implementation TouchesView
- (id)initWithFrame:(CGRect)frame
{
self = [super initWithFrame:frame];
if (self) {
[self setUserInteractionEnabled:YES];
[self setBackgroundColor:[UIColor colorWithRed:1.0f green:0.6f blue:0.6f alpha:1.0f]];
}
return self;
}
- (void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event
{
CGPoint touch = [[touches anyObject] locationInView:self];
NSLog(#"(%.1f, %.1f)", touch.x, touch.y);
NSLog(#"%#", NSStringFromCGPoint(touch));
}
- (void)touchesMoved:(NSSet *)touches withEvent:(UIEvent *)event{}
- (void)touchesEnded:(NSSet *)touches withEvent:(UIEvent *)event{}
- (void)touchesCancelled:(NSSet *)touches withEvent:(UIEvent *)event{}
#end
And this is the initialization and adding it to a view controller:
TouchesView *touchView = [[TouchesView alloc] initWithFrame:self.view.bounds];
[self.view addSubview:touchView];
This works fine for me.
The -20 offset on the y values is fixed by setting "Status bar is initially hidden" to YES. The rest of the problem looks like it's a hardware issue because I got a different range for x and y values each time I run the program on the iPad. I didn't get this problem while running the program on the simulator. On the simulator, x values range from 1 to 767 and y values range from 1 to 1023, which are pretty correct.

Drag an UIImageView in a restricted range

How is it possible to drag a UIImageView but only within a certain area of the screen? My code currently looks like this:
- (void) touchesMoved:(NSSet *)touches withEvent:(UIEvent *)event{
UITouch *touch = [[event allTouches] anyObject];
touch.view.frame = CGRectMake(104, 171, 113, 49);
if([touch view] == toggle)
{
CGPoint location = [touch locationInView:self.view];
CGPoint newLocation = CGPointMake(toggle.center.x, location.y);
toggle.center = newLocation;
NSLog(#"%f \n",toggle.center.y);
}
}
I want to be able to drag the image only within the frame i have defined.
You can use CGRectContainsRect(rect1, rect2) to check if the first rect is completely inside of the second
bool CGRectContainsRect (
CGRect rect1,
CGRect rect2
);
When you are using UIViews and want to see if one view falls completely within the frame of a second, a related function CGRectContainsRect will do the checking for you. This does not check for an intersection; the union of both rectangles must be equal to the first rectangle for this to return true. The function takes two arguments. The first rectangle is always the surrounding item. The second argument either falls fully inside the first or it does not.
So your code could be something like this
CGPoint location = [touch locationInView:self.view];
CGPoint newLocation = CGPointMake(toggle.center.x, location.y);
CGRect r = CGRectMake(newLocation.x-self.frame.size.width/2,
newLocation.y-self.frame.size.height/2,
self.frame.size.width,
self.frame.size.height);
if(CGRectContainsRect(r, theOtherRect)){
toggle.center = newLocation;
NSLog(#"%f \n",toggle.center.y);
}
Other useful CoreGraphics functions: http://blogs.oreilly.com/iphone/2008/12/useful-core-graphics-functions.html
Another hint:NSLog(#"%#", NSStringFromCGPoint(toggle.center)) makes logging of CGTypes easier. use equivalently: NSStringFromCGRect(rect)
if (newLocation.y > NSMaxY(touch.view.frame)) newLocation.y = NSMaxY(touch.view.frame);
if (newLocation.y < NSMinY(touch.view.frame)) newLocation.y = NSMinY(touch.view.frame);
toggle.center = newLocation;
You can do the same for the x coordinate if you like.

UIView drag (image and text)

Is it possible to drag UIView around the iOS screen while it has both image and text? e.g. small cards. Could you point me to the similar (solved) topic? I haven't found any.
This is what a neat solution, based on pepouze's answer, would look like (tested, it works!)
- (void) touchesMoved:(NSSet *)touches withEvent:(UIEvent *)event
{
UITouch *aTouch = [touches anyObject];
CGPoint location = [aTouch locationInView:self];
CGPoint previousLocation = [aTouch previousLocationInView:self];
self.frame = CGRectOffset(self.frame, (location.x - previousLocation.x), (location.y - previousLocation.y));
}
While UIView does not have a built-in support for moving itself along the user dragging, it should be not so difficult to implement it. It is even easier when you are only dealing with dragging on the view, and not other actions such as tapping, double tapping, multi-touches etc.
First thing to do is to make a custom view, say DraggableView, by subclassing UIView. Then override UIView's touchesMoved:withEvent: method, and you can get a current dragging location there, and move the DraggableView. Look at the following example.
-(void) touchesMoved:(NSSet *)touches withEvent:(UIEvent *)event {
UITouch *aTouch = [touches anyObject];
CGPoint location = [aTouch locationInView:self.superview];
[UIView beginAnimations:#"Dragging A DraggableView" context:nil];
self.frame = CGRectMake(location.x, location.y,
self.frame.size.width, self.frame.size.height);
[UIView commitAnimations];
}
And because all subviews of the DraggableView object will be moved, too. So put all your images and texts as subviews of the DraggableView object.
What I implemented here is very simple. However, if you want more complex behaviors for the dragging, (for example, the user have to tap on the view for a few seconds to move the view), then you will have to override other event handling methods (touchesBegan:withEvent: and touchesEnd:withEvent) as well.
An addition to MHC's answer.
If you don't want the upper left corner of the view
to jump under your finger, you can also override touchesBegan
like this:
- (void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event
{
UITouch *aTouch = [touches anyObject];
offset = [aTouch locationInView: self];
}
and change MHC's touchesMoved to:
-(void) touchesMoved:(NSSet *)touches withEvent:(UIEvent *)event
{
UITouch *aTouch = [touches anyObject];
CGPoint location = [aTouch locationInView:self.superview];
[UIView beginAnimations:#"Dragging A DraggableView" context:nil];
self.frame = CGRectMake(location.x-offset.x, location.y-offset.y,
self.frame.size.width, self.frame.size.height);
[UIView commitAnimations];
}
you should also define CGPoint offset in the interface:
#interface DraggableView : UIView
{
CGPoint offset;
}
EDIT:
Arie Litovsky provides more elegant solution that allows you to ditch the ivar: https://stackoverflow.com/a/10378382/653513
Even though rokjarc solution works, using
CGPoint previousLocation = [aTouch previousLocationInView:self.superview];
avoids the CGPoint offset creation and the call to touchesBegan:withEvent:
Here is a solution to drag a custom UIView (it can be scaled or rotated through its transform), which can hold images and/or text (just edit the Tile.xib as required):
- (void) touchesMoved:(NSSet*)touches withEvent:(UIEvent*)event
{
UITouch *touch = [touches anyObject];
CGPoint location = [touch locationInView:self];
CGPoint previous = [touch previousLocationInView:self];
if (!CGAffineTransformIsIdentity(self.transform)) {
location = CGPointApplyAffineTransform(location, self.transform);
previous = CGPointApplyAffineTransform(previous, self.transform);
}
self.frame = CGRectOffset(self.frame,
(location.x - previous.x),
(location.y - previous.y));
}
This work for me. My UIView rotated and scaled
- (void) touchesMoved:(NSSet<UITouch *> *)touches withEvent:(UIEvent *)event{
UITouch *touch = [touches anyObject];
CGPoint location = [touch locationInView:self];
CGPoint previous = [touch previousLocationInView:self];
if (!CGAffineTransformIsIdentity(self.transform)) {
location = CGPointApplyAffineTransform(location, self.transform);
previous = CGPointApplyAffineTransform(previous, self.transform);
}
CGRect newFrame = CGRectOffset(self.frame,
(location.x - previous.x),
(location.y - previous.y));
float x = CGRectGetMidX(newFrame);
float y = CGRectGetMidY(newFrame);
self.center = CGPointMake(x, y);
}