📜 ⬆️ ⬇️

Wean the touch screen to shift the coordinates of the touch a couple of mm up

I think very few people have noticed that the physical coordinates of the touch of a finger and their program display in iOS are slightly different: iOS gives a point displaced by about 1.5 mm upwards relative to the real touch. This is done in the interest of usability - a point close to the nail seems more realistic than lying below the pad of the finger. In addition, you can better see the area of ​​the screen where you click.
To make it clearer what you are talking about, you can download any drawing artist (for example, Bamboo Paper , the app is not mine, free ), block the screen auto-rotate, draw a small horizontal line, then turn the device upside down (always when blocking the auto-rotate) and try to continue the drawn line. Most likely the continued line will be lower than the original.

In most applications, this behavior is invisible, but in some it can be harmful. For example, in pictures, where auto-rotate is disabled and it is assumed that the user can rotate the device as he pleases, it is necessary to ensure identical handling of touches, regardless of the current rotation of the device. Or in a board game, such as chess, where the displacement of coordinates can lead to the player playing black (and for which the interface in portrait mode is upside down), when you try to move a piece, it will miss and take the piece from the cage below.

According to my tests on the first generation iPad, the shift was about 7 pixels. Naturally, on other devices, the result will be different, it is necessary to test.

I did not find an open software interface for managing this shift in the Apple documentation, and therefore I had to look for workarounds.
')
In the case of drawing painters, you can simply introduce an amendment to the logic of methods touchesMoved and the like:
- (void)touchesMoved:(NSSet *)touches withEvent:(UIEvent *)event { //... CGPoint point = [[touches anyObject] locationInView:self]; //    , ..  y    point.y = point.y + 7; //... } 

However, this is not always acceptable, especially if there is a certain amount of ready-made code that you want to reuse without changes.

As a radical solution in the first version of this article, I showed an example with an amendment to absolutely all the calls of locationInView and previousLocationInView in UITouch instances:
 #import "objc/runtime.h" @interface UITouch (Adjusted) -(CGPoint)adjustedLocationInView:(UIView *)view; -(CGPoint)adjustedPreviousLocationInView:(UIView *)view; @end @implementation UITouch (Adjusted) -(CGPoint)adjustedLocationInView:(UIView *)view{ CGPoint point = [self adjustedLocationInView:view]; point.y = point.y + 7; return point; } -(CGPoint)adjustedPreviousLocationInView:(UIView *)view{ CGPoint point = [self adjustedPreviousLocationInView:view]; point.y = point.y + 7; return point; } +(void)load{ Class class = [UITouch class]; Method locInViewMethod = class_getInstanceMethod(class, @selector(locationInView:)); Method adjLocInViewMethod = class_getInstanceMethod(class, @selector(adjustedLocationInView:)); method_exchangeImplementations(locInViewMethod, adjLocInViewMethod); Method prevLocInViewMethod = class_getInstanceMethod(class, @selector(previousLocationInView:)); Method adjPrevLocInViewMethod = class_getInstanceMethod(class, @selector(adjustedPreviousLocationInView:)); method_exchangeImplementations(prevLocInViewMethod, adjPrevLocInViewMethod); NSLog(@"UITouch class is adjusted now."); } @end 

Here, using the runtime function method_exchangeImplementations, the implementations of the default UITouch methods are exchanged for our corrections. Please note that the file with the created category does not have to be imported into any other .h or .m file. Just add it to the project, and the + load method will be called automatically, because the + load message is automatically sent to each class and category when added to the runtime.
Upd. However, as it turned out in the comments , starting with iOS 5, Apple asks not to use method_exchangeImplementations in applications. Thanks wicharek for the info. Therefore, to amend, it is better to use any other method to your taste, for example, one of those proposed in the comments.

So, after the introduction of the amendment, drawing on the view will work identically for any orientation of the device. However, this does not solve problems with chess pieces: in determining which view to send a touch event, the point with a shift will still be used, and we can get into the wrong figure.
To determine which view should handle the touch, use the hitTest: withEvent: method of UIView , which works like this:

Thus, for the correct hit of the touches in the chess figures, it is enough to override hitTest: withEvent at the root view, which contains all the subviews for which we need to introduce an amendment:
 @implementation ChessBoardView - (UIView*)hitTest:(CGPoint)point withEvent:(UIEvent *)event { point.y = point.y + 7; UIView *hitView = [super hitTest:point withEvent:event]; return hitView; } @end 

After that, the player who plays black gets into the chess pieces as comfortably as the player who plays white.

Source: https://habr.com/ru/post/131947/


All Articles