iOS Touches (Auto) Offset on iPad
Asked Answered
P

2

7

There is almost no information out there about this, so here is my problem:

I'm working on a 2 player 'sit across from eachother' ipad game and the player who is on the opposite side has a hard time tapping buttons... because it appears that Apple auto-shifts the touch based on the device orientation (which is great for regular use or the main player). Is there any way to reset it or tell the ipad to not do that? (Btw iPhone does this too)

This is all I have found: Given this apparent inconsistency, I've decided to just cheat it by opening up the moving parts' 'bounds' so that there is an area for the user to hit below the part's area. On the whole, users seem to hit below an object rather than above it - so don't notice the shifting caused by iOS as much.

So if there is no easy way to prevent the offsetting, is the only solution to shift each uiview's (or subclass's) bounds for the opposite player? Or is that even correct?

Any answers appreciated.

Pure answered 24/10, 2011 at 4:16 Comment(0)
P
4

EDIT: Omg you guys I finally found a way! I made a subclass of UIView, and made my game view (which houses all of the UIScrollViews, UIButtons, etc) use that subclass. And then this was the only code I added to the subclass:

-(UIView *)hitTest:(CGPoint)point withEvent:(UIEvent *)event
{
    CGPoint touchSpot = point;
    if (![[[UIDevice currentDevice] model] isEqualToString:@"iPad Simulator"] && touchSpot.y < 512)
    {
        touchSpot = CGPointMake(touchSpot.x, touchSpot.y+10);
    }

    UIView *hitView = [super hitTest:touchSpot withEvent:event];

    if (hitView == self)
        return self; // you can return nil here if you want touches to go through this view (ie transparent space in a HUD overlay or w/e)

    return hitView;
}

So far this has worked nicely... the person sitting on the opposite end has good touch response just like the player on the "home" side. Beware of presenting views on top of this view though... everything will go through that method...

Old solution (before I found that out ^):

Well, right now my solution has been to subclass uigesturerecognizer, forward ANY touch (ie no constraints on recognizer), then add it to my game's view. Then the method that responds to any touch only runs when the state is Ended, and if so, it then proceeds to loop through every subview (not many) to see if the touch intersected one. As well, for the opposite player it compares the rect offsetted a bit (for each of the subviews).

I'll see if I can find a more efficient way... but so far okay.

I could have also made my game-view a uiview subclass and overridden touchesbegan/moved/ended but I already had it all placed out, so I went with the recognizer (thus not having to subclass uiview).

Pure answered 31/10, 2011 at 6:1 Comment(0)
C
4

Yes, the touch offset does exist, its size is not documented and it cannot be cancelled (at least, with public API).

You have to reverse-engineer the offset itself. I would just display a small circle where UIKit says the touch is, and then adjust it by trial and error. When you have the offset, you can manually apply it (-2 * offset) in the event handlers for the opposite player only.

I can't tell how right it will feel, you have to experiment a bit.

Clone answered 24/10, 2011 at 5:0 Comment(5)
Right for touches I track myself I can do that, but what about for UIButtons or stuff on a tableview which are not as much in my control? They already weed out a missed touch. Any suggestion for this? Thanks.Pure
To affect all touches you would have to override sendEvent: at the UIWindow/UIApplication level, but you cannot modify UITouches anyway. The problem here is that in order to "fix" Apple's touch adjustment machinery (which most likely happens at the driver level) everywhere you have to use private APIs like described here: cocoawithlove.com/2008/10/…, but I would not recommend it because it's a very, very dangerous hack.Clone
Darn okay. Well I'm looking into a view's Bounds... I wonder if I could turn off clipping, then offset a view's bounds by -10 pixels, and then offset a view's frame by +10 pixels (frames for 2nd player). Though any collision/position stuff I'd have to remember to check -10 pixels on the frame.Pure
@Pure You can affect hit-testing, but once, say, a button starts receiving touch events, it will process them normally. Every sane implementation of a UIView subclass uses [UITouch locationInView: self], which handles bounds offset and even superlayer's sublayerTransform correctly. I suspect you can cheat the touch offset algorithm only in the simplest case: when you don't use any standard controls, e.g. in a pure OpenGL game.Clone
I am really frustrated with this offset. It is really affecting my stylus precision in all handwriting apps. I would be really glad if there was a way to turn it off in iOS settings.Newcastle
P
4

EDIT: Omg you guys I finally found a way! I made a subclass of UIView, and made my game view (which houses all of the UIScrollViews, UIButtons, etc) use that subclass. And then this was the only code I added to the subclass:

-(UIView *)hitTest:(CGPoint)point withEvent:(UIEvent *)event
{
    CGPoint touchSpot = point;
    if (![[[UIDevice currentDevice] model] isEqualToString:@"iPad Simulator"] && touchSpot.y < 512)
    {
        touchSpot = CGPointMake(touchSpot.x, touchSpot.y+10);
    }

    UIView *hitView = [super hitTest:touchSpot withEvent:event];

    if (hitView == self)
        return self; // you can return nil here if you want touches to go through this view (ie transparent space in a HUD overlay or w/e)

    return hitView;
}

So far this has worked nicely... the person sitting on the opposite end has good touch response just like the player on the "home" side. Beware of presenting views on top of this view though... everything will go through that method...

Old solution (before I found that out ^):

Well, right now my solution has been to subclass uigesturerecognizer, forward ANY touch (ie no constraints on recognizer), then add it to my game's view. Then the method that responds to any touch only runs when the state is Ended, and if so, it then proceeds to loop through every subview (not many) to see if the touch intersected one. As well, for the opposite player it compares the rect offsetted a bit (for each of the subviews).

I'll see if I can find a more efficient way... but so far okay.

I could have also made my game-view a uiview subclass and overridden touchesbegan/moved/ended but I already had it all placed out, so I went with the recognizer (thus not having to subclass uiview).

Pure answered 31/10, 2011 at 6:1 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.