简体   繁体   中英

Using CGAffineTransformScale with UIAttachmentBehavior (UIDynamicAnimator)

In a subclass of UIButton, I attach the UIButton to a UIAttachmentBehavior that lets a user drag the button around the screen with their finger.

In - (void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event I add the button to the UIAttachmentBehavior, then add the behavior to the UIDynamicAnimator. During - (void)touchesMoved:(NSSet *)touches withEvent:(UIEvent *)event I update the anchor of the UIAttachmentBehavior to the touch point; this creates the desired drag effect.

Now I wish to use CGAffineTransformScale to increase the size of the button when the touch begins so the user can see the button under their finger. My issue is that the transform I apply with CGAffineTransformScale is immediately over written the second I add the attachment behavior. The result is a quick flicker of the button scaling up, but then it returns back to the original size.

I have tried [_animator removeAllBehaviors] before applying the CGAffineTransformScale, then adding the behaviors back. I have also tried [_animator updateItemUsingCurrentState:self] after applying the CGAffineTransformScale, just before adding the attachment behavior. Neither resolve the issue.

UPDATE 1 : Thinking about HalR's answer below, I decided to try applying the scale transform with every touch. So, I added the CGAffineTransformScale call to both the touchesMoved: and touchesEnded . I am using CGAffineTransformScale vs CGAffineTransformMakeScale because it allows me to preserve the slight rotation the attachment behavior adds. It got me a lot closer. The button now moves around the screen while being scaled. It isn't perfect though. There is a flicker when you are not moving around the screen, and if you stop moving, but keep the touch down, the button returns to the original size. Almost there...any suggestions?

Here is my updated code:

@interface DragButton : UIButton < UIDynamicAnimatorDelegate >

#import "DragButton"
#import <QuartzCore/QuartzCore.h>

@implementation DragButton

- (void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event
{
    [super touchesBegan:touches withEvent:event];

    UITouch *touch = [[event allTouches] anyObject];
    CGPoint touchLocation = [touch locationInView:self.referenceView];

    self.transform = CGAffineTransformMakeScale(1.5, 1.5);

    _touchAttachmentBehavior = [[UIAttachmentBehavior alloc] initWithItem:self attachedToAnchor:touchLocation];
    [_animator addBehavior:_touchAttachmentBehavior];
}

- (void)touchesMoved:(NSSet *)touches withEvent:(UIEvent *)event
{
    [super touchesMoved:touches withEvent:event];

    UITouch *touch = [[event allTouches] anyObject];
    CGPoint touchLocation = [touch locationInView:self.referenceView];

    self.transform = CGAffineTransformScale(self.transform, 1.5, 1.5);

    _touchAttachmentBehavior.anchorPoint = touchLocation;
}

- (void)touchesEnded:(NSSet *)touches withEvent:(UIEvent *)event
{
    [super touchesEnded:touches withEvent:event];

    self.transform = CGAffineTransformScale(self.transform, 1.5, 1.5);

    [_animator removeBehavior:_touchAttachmentBehavior];
}

How does UIAttachmentBehavior affect a view?

It does it by modifying the view's transform.

In this Ray Wenderlich tutorial Colin Eberhardt logs the transform as the view is affected by the behavior.

Even though the transform is never set, or modified directly, it changes as the view is moved by the behavior.

So you can't set your transform, and hope for it to stay set, because it is being set by the behavior.

In a side note, if you are trying to set the transform to scale by 1.5, you should use this:

self.transform = CGAffineTransformMakeScale(1.5, 1.5);

otherwise every time your touchesBegan is called it will grow by another 50%.

In the documentation for UIDynamicAnimator, its says:

"A dynamic animator automatically reads the initial state (position and rotation) of each dynamic item you add to it, and then takes responsibility for updating the item's state. If you actively change the state of a dynamic item after you've added it to a dynamic animator, call this method to ask the animator to read and incorporate the new state."

So just call your transform after adding the behavior, then call updateItemUsingCurrentState:

- (void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event
{
    [super touchesBegan:touches withEvent:event];

    UITouch *touch = [[event allTouches] anyObject];
    CGPoint touchLocation = [touch locationInView:self.referenceView];

    _touchAttachmentBehavior = [[UIAttachmentBehavior alloc] initWithItem:self attachedToAnchor:touchLocation];
    [_animator addBehavior:_touchAttachmentBehavior];
    self.transform = CGAffineTransformMakeScale(1.5, 1.5);
    [_animator updateItemUsingCurrentState:self];
}

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM