質問

I've been trying to figure this out for hours, completely at a loss here. I'm trying to implement a UIPinchGestureRecognizer for some of the custom UIImageViews in my game, but it doesn't work. Everything thing I've researched says it should work, yet it doesn't. Pinch works fine if I add it to my view controller, or to a custom UIView, but not the UIImageViews. I've tried all the common fixes and tweaks, to no success. I have userInteractionEnabled and multipleTouchEnabled set to YES. I have the delegate and selectors set up properly. I have shouldRecognizeSimultaneouslyWithGestureRecognizer set to return YES.

The gesture recognizer is getting added to the UIImageView, I've been able to access its properties later in my update loop, but the NSLog in the selector never gets called for the UIImageView when I try to pinch. I've adjusted the z-position of the views to ensure they are on top but no dice.

My UIImageViews are stored in a NSMutableDictionary and are updated by looping through it during each update loop of the game. Could this have an effect on the UIPinchGestureRecognizer not getting called?... I can't think of anything else and posting the code probably won't help - because the same exact code works when it's used for the UIView or view controller.

I do have touch handling code in the view controller's touchesBegan and touchedMoved events... but I've turned that off but the problem still persists, and the pinch worked for other elements with it on anyway.

Any ideas what could prevent a gesture selector from firing on an UIImageView? The dictionary? Something to do with being constantly updated in the game loop? Any ideas would be welcome, this seems so simple to implement...

Edit: Here's the code for the UIImageView and what I'm doing with it... not sure if this helps.

Extended UIImageView class Paper.m (prp is a struct of properties used to initialize my custom variables:

NSString *tName =  [NSString stringWithUTF8String: prp.imagePath];
UIImage *tImage = [UIImage imageNamed:[NSString stringWithFormat:@"%@.png",tName]];
self = [self initWithImage: tImage];
self.userInteractionEnabled = YES;
self.multipleTouchEnabled = YES;
self.center = CGPointMake(prp.spawnX, prp.spawnY);
if (prp.zPos != 0)  { self.layer.zPosition = prp.zPos; }
// other initialization excised

Then I have a custom class called ObjManager that holds the NSMutableDictionary and initializes all UIImageView objects like so, where addObj is called in a loop to add each object:

- (ObjManager*) initWithBlank {
    // create an array for our objects
    self = [super init];
    if (self) {
        objects = [[NSMutableDictionary alloc] init];
        spawnID = 100;  // start of counter for dynamically spawned object IDs
    }
    return self;
}

- (void) addObj:(Paper *)paperPiece wasSpawned:(BOOL)spawned {
    // add each paper piece, assign spawnID if dynamically spawned
    NSNumber *newID;
    if (spawned) { newID = [NSNumber numberWithInt:spawnID]; spawnID++; }
    else         { newID = [NSNumber numberWithInt:paperPiece.objID]; }
    [objects setObject:paperPiece forKey:newID];
}

My view controller calls the initialization of the ObjManager (called _world in my VC). Then it loops through _world like so:

// Populate additional object managers and add all subviews
for (NSNumber *key in _world.objects) {

    _eachPiece = [_world.objects objectForKey:key];

    // Populate collision object manager
    if (_eachPiece.collision) {
        [_world_collisions addObj:_eachPiece wasSpawned:NO];
    }

    // only add pinch gesture if the object flag is set
    if (_eachPiece.pinch) {  
        UIPinchGestureRecognizer *pinchGesture = [[UIPinchGestureRecognizer alloc] initWithTarget:self action:@selector(pinchPaper:)];
        pinchGesture.delegate = self;
        [_eachPiece addGestureRecognizer:pinchGesture];
        NSLog(@"Added pinch recognizer scale: %@", pinchGesture.view.description);
    }

    // Add each object as a subview
    [self.view addSubview:_eachPiece];

}

_eachPiece is an object in my view controller, declared in the .h file (as is _world):

@property (nonatomic, strong) ObjManager *world; 
@property (nonatomic, strong) Paper *eachPiece;

Then I have an NSTimer object that updates all moveable Paper objects (the UIImageViews) in _world (ObjManager) every frame like so:

// loop through each piece and update
for (NSNumber *key in _world.objects) {

    eachPiece = [_world.objects objectForKey:key];

    // only update moveable pieces
    if ((eachPiece.moveType == Move_Touch) || (eachPiece.moveType == Move_Auto)) {

        CGPoint paperCenter;
        paperCenter = eachPiece.center;

        // a bunch of code to update paperCenter x & y for the object's new position based on velocity and user input

        // determine image direction and transformation matrix
        [_world updateDirection:eachPiece];
        CGAffineTransform transformPiece = [_world imageTransform:eachPiece];
        if (transformEnabled) {
            eachPiece.transform = transformPiece;
        }

        // finally move it
        [eachPiece setCenter:paperCenter];

    }

}

And the pinch selector:

- (void)pinchPaper:(UIPinchGestureRecognizer *)recognizer {
    NSLog(@"Pinch scale: %f", recognizer.scale);
    recognizer.view.transform = CGAffineTransformScale(recognizer.view.transform, recognizer.scale, recognizer.scale);
    recognizer.scale = 1;
}

As far as I can tell, the pinch should work. If I take the same pinch gesture code and set it to add to the view controller, it works for the entire view. I also have a custom UIView class that acts as a border (simply a rectangle drawn around the view), and moving the pinch gesture code to that allows me to pinch the border only.

役に立ちましたか?

解決

Alright, so apparently gesture recognizers don't fire on views where the position is being animated. So to make it work I had to put the recognizer on the view controller, then perform a hit test and apply pinch/zoom on the touched view if it's one I want to pinch/zoom. Info on that here:

http://iphonedevsdk.com/forum/iphone-sdk-tutorials/100982-caanimation-tutorial.html

For my particular case, I kept track of which animated views I wanted to pinch, in a variable/array at the View Controller level. Then I used this code in the selector (essentially from the link above, all credit to them):

- (void)pinchPaper:(UIPinchGestureRecognizer *)recognizer {

    CALayer *pinchLayer;
    id layerDelegate;
    CGPoint touchPoint = [recognizer locationInView:self.view];

    pinchLayer = [self.view.layer.presentationLayer hitTest: touchPoint];
    layerDelegate = [pinchLayer delegate];

    //_pinchView is the UIView I want to pinch
    if (layerDelegate == _pinchView) {
        _pinchView.transform = CGAffineTransformScale(_pinchView.transform, recognizer.scale, recognizer.scale);
        recognizer.scale = 1;
    }
}

Only tricky thing is if you have other scale transforms (like changing directions in mine) going on as part of the existing UIView animation, you have to account for that, by using the current transform during each update loop.

他のヒント

For any gesture recognizer to work on imageViews, userInteraction must be enabled on it.

So, it should be,

yourImageView.userInteractionEnabled = YES;

Or, if you are using storyboards, you can check that option in storyboard's inspector window too.

Hope it helps..:)

ライセンス: CC-BY-SA帰属
所属していません StackOverflow
scroll top