I'm using UIDynamicsAnimator with good results except that after the animations are done the image that have been pushed and bounced is blurry. Please help me.
#property (nonatomic,weak) IBOutlet UIImageView *someImage;
self.animator = [[UIDynamicAnimator alloc] initWithReferenceView:self.view];
self.gravity = [[UIGravityBehavior alloc] initWithItems:#[self.someImage]];
CGVector gravityDirection = {-1.0, 0.5};
[self.gravity setGravityDirection:gravityDirection];
self.collision = [[UICollisionBehavior alloc] initWithItems:#[self.someImage]];
self.collision.translatesReferenceBoundsIntoBoundary = YES;
self.itemBehaviour = [[UIDynamicItemBehavior alloc] initWithItems:#[self.someImage]];
self.itemBehaviour.elasticity = 0.5;
[self.animator removeAllBehaviors]; // to avoid problems from the last behaviour animation
[self.collision addBoundaryWithIdentifier:#"barrier"
fromPoint:CGPointMake(444,0)
toPoint:CGPointMake(444,768)];
self.pushBehavior = [[UIPushBehavior alloc] initWithItems:#[self.someImage] mode:UIPushBehaviorModeInstantaneous];
self.pushBehavior.magnitude = 1.0f;
self.pushBehavior.angle = 0.0f;
[self.animator addBehavior:self.pushBehavior];
self.pushBehavior.pushDirection = CGVectorMake(1.0f, 0.0f)
self.pushBehavior.active = YES;
[self.animator addBehavior:self.itemBehaviour];
[self.animator addBehavior:self.collision];
[self.animator addBehavior:self.gravity];
I think your problem is that the image isn't on an exact point when the animation is done. I would sugest that you add a delegate and when the animation pauses you round the x and y for the image to the closest integer.
Related
The story is that, the user selects a part of an image by drawing lines or curves. Based on the selection, a mask will be created using an edge detector.
How can i achieve this?
Are CoreImage and GPUImage could help to make this possible?
Is edge detection enough to make a mask?
I don't have any advanced knowledge on image processing and manipulation. I am just a starter trying to understand and learn it. By the way, this should be done in iOS.
Just like this one (the masking part).
I wrote a sample for you to show you how to use mask to select part of image, if you want to implement more complex effect, you just need to change the path, then you can get any shape you want.
#interface MainViewController ()
#property UIImageView* imageV;
#property UIView* selectView;
#property UIView *blockView;
#property CGPoint startPoint;
#property CGPoint endPoint;
#end
#implementation MainViewController
- (void)viewDidLoad {
[super viewDidLoad];
// Do any additional setup after loading the view.
UIBarButtonItem *recoverItem = [[UIBarButtonItem alloc] initWithTitle:#"Recover" style:UIBarButtonItemStyleDone target:self action:#selector(recover)];
[self.navigationItem setLeftBarButtonItem:recoverItem];
_imageV = [[UIImageView alloc] initWithFrame:[UIScreen mainScreen].bounds];
_imageV.image = [UIImage imageNamed:#"test"];
[self.view addSubview:_imageV];
_blockView = [[UIView alloc] init];
_blockView.frame = _imageV.bounds;
_blockView.backgroundColor = [UIColor whiteColor];
[_imageV addSubview:_blockView];
_blockView.hidden = true;
_selectView = [[UIView alloc] init];
_selectView.layer.borderColor = [UIColor greenColor].CGColor;
_selectView.layer.borderWidth = 2;
[_imageV addSubview:_selectView];
_selectView.hidden = true;
UIPanGestureRecognizer *panGR = [[UIPanGestureRecognizer alloc] initWithTarget:self action:#selector(handlePan:)];
[self.view addGestureRecognizer:panGR];
}
- (void) handlePan: (UIPanGestureRecognizer *)pan{
if (UIGestureRecognizerStateBegan == pan.state) {
_startPoint = [pan locationInView:_imageV];
}
else if (UIGestureRecognizerStateChanged == pan.state) {
_selectView.hidden = false;
CGPoint currentP = [pan locationInView:_imageV];
float rectWidth = currentP.x - _startPoint.x;
float rectHeight = currentP.y - _startPoint.y;
_selectView.frame = CGRectMake(_startPoint.x, _startPoint.y, rectWidth,rectHeight);
}
else if (UIGestureRecognizerStateEnded == pan.state) {
_endPoint = [pan locationInView:_imageV];
float rectWidth = _endPoint.x - _startPoint.x;
float rectHeight = _endPoint.y - _startPoint.y;
//create path
UIBezierPath *maskPath = [UIBezierPath bezierPathWithRect:[UIScreen mainScreen].bounds];
UIBezierPath *otherPath = [[UIBezierPath bezierPathWithRect:CGRectMake(_startPoint.x, _startPoint.y, rectWidth,rectHeight)] bezierPathByReversingPath];
[maskPath appendPath:otherPath];
//set mask
CAShapeLayer *maskLayer = [CAShapeLayer layer];
maskLayer.path = maskPath.CGPath;
[_blockView.layer setMask:maskLayer];
_blockView.hidden = false;
_selectView.hidden = true;
}
}
-(void) recover{
_blockView.hidden = true;
}
#end
Hope it can help you.
i create UIDynamicAnimator and UIGravityBehavior add gravity to animator but gravity doesn't work. and there display my dropView.
-(UIDynamicAnimator *)animaotr
{
if(!_animator)
{
_animator = [[UIDynamicAnimator alloc] initWithReferenceView:self.gameView];
}
return _animator;
}
-(UIGravityBehavior *)gravity
{
if(!_gravity)
{
_gravity = [[UIGravityBehavior alloc] init];
_gravity.magnitude = 1;
[self.animator addBehavior:_gravity];
}
return _gravity;
}
-(void)drop{
CGRect frame;
frame.origin = CGPointZero;
frame.size = DROP_SIZE;
int x = (arc4random()%(int)self.gameView.bounds.size.width) / DROP_SIZE.width;
frame.origin.x = x * DROP_SIZE.width;
UIView *dropView = [[UIView alloc] initWithFrame:frame];
dropView.backgroundColor = [self randomColor];
[self.gameView addSubview:dropView];
[self.gravity addItem:dropView];
}
The problem is this declaration:
-(UIDynamicAnimator *)animaotr {
That method doesn't match the name self.animator, so it won't be called, and thus your gravity behavior is not being added to any animator.
I tried to set an block on the action property, but that didn't work... any ideas? I know the UIView animation... method has a completion block, but not sure about the UIDynamicAnimations.
EDIT: adding code
[self.animator removeAllBehaviors];
UIGravityBehavior *gravityBehaviour = [[UIGravityBehavior alloc] initWithItems:#[self.onscreen]];
gravityBehaviour.gravityDirection = CGVectorMake(0, 10);
gravityBehaviour.action = ^{
if(self.onscreen.frame.origin.y > [UIScreen mainScreen].bounds.size.height)
[self.onscreen removeFromSuperview];
NSLog(#"locations is %f, height is %f", self.onscreen.frame.origin.y, [UIScreen mainScreen].bounds.size.height);
};
[self.animator addBehavior:gravityBehaviour];
UIDynamicItemBehavior *itemBehaviour = [[UIDynamicItemBehavior alloc] initWithItems:#[self.onscreen]];
[itemBehaviour addAngularVelocity:-M_PI_2 forItem:self.onscreen];
[self.animator addBehavior:itemBehaviour];
The output reflected that the view just continued to be moved even while off screen.
So I solved it by throwing one more thing in the if statement: [animator removeAllBehaviors]; that seemed to do the trick.
let dynamicBehaviour = UIDynamicItemBehavior(items: [randomWord])
weak var weakBehaviour = dynamicBehaviour
weak var weakSelf = self
dynamicBehaviour.action = {
if let currentY = weakBehaviour?.linearVelocityForItem(rndWord).y {
if currentY > CGRectGetMaxY(self.view.frame) / 2 {
weakSelf?.animator?.removeAllBehaviors()
weakSelf?.randomWord?.removeFromSuperview()
}
}
}
I am just starting to use the UIDynamic kit and have followed examples online. I have achieved the behaviour I want, which is for a view to recieved an instantaneous force in a direction and spring back to the original spot.
I achieved this using the code below
CGPoint origCenter = self.viewContentContainer.center;
self.animator = [[UIDynamicAnimator alloc] initWithReferenceView:self.view];
UIPushBehavior *push = [[UIPushBehavior alloc]
initWithItems:#[self.viewContentContainer] mode:UIPushBehaviorModeInstantaneous];
CGVector vector = CGVectorMake(200.0, 0.0);
push.pushDirection = vector;
CGPoint anchorpoint = origCenter;
UIAttachmentBehavior *attachment = [[UIAttachmentBehavior alloc] initWithItem:self.viewContentContainer attachedToAnchor:anchorpoint];
[attachment setFrequency:2.0];
[attachment setDamping:0.5];
[self.animator addBehavior:push];
[self.animator addBehavior:attachment];
However, it oscillates for a very long time at the end over 1 or 2 pixels. Changing the frequency and damping values doesn't seem to make this any better or worse.
Has anyone else experienced this?
Many Thanks.
Add this and it should fix it:
attachment.length = 1.0f;
It is an expected behavior, although it seems "unsightly". The above just offsets that "unsightliness".
Tested. Hope it helps.
i have the same problem. didn't solve it yet, but i have some ideas
i remove attachment behavior when UIGestureRecognizer ended,
but when i drag it still oscillates :(
-(void) handlePanGestureRecognizer:(UIPanGestureRecognizer*) gr
{
CGPoint att = [gr locationInView:self.view];
self.attachView.center = att;
if (gr.state == UIGestureRecognizerStateBegan)
{
_attachment.anchorPoint = att;
[_animator addBehavior:_attachment];
if (_snap)
[_animator removeBehavior:_snap];
}
if (gr.state == UIGestureRecognizerStateChanged)
{
_attachment.anchorPoint = att;
}
if (gr.state == UIGestureRecognizerStateEnded)
{
_snap = [[UISnapBehavior alloc] initWithItem:self.button snapToPoint:att];
[_animator addBehavior:_snap];
[_animator removeBehavior:_attachment];
}
}
and this is not pretty solution but it works as i like
-(void) handlePanGestureRecognizer:(UIPanGestureRecognizer*) gr
{
CGPoint att = [gr locationInView:self.view];
self.attachView.center = att;
if (_snap)
[_animator removeBehavior:_snap];
_snap = [[UISnapBehavior alloc] initWithItem:self.button snapToPoint:att];
[_animator addBehavior:_snap];
}
The way I got around these oscillations was by removing the behavior entirely, using animator.removeBehavior() and then re-adding the same behavior back to the animator.
Add this behaviour:
dynamic = UIDynamicItemBehavior()
dynamic.resistance = 1
self.animator.addBehavior(dynamic)
I have the following code which simply attaches a password UITextField to an anchorPoint. When I run this nothing happens.
UIDynamicItemBehavior *behavior =
[[UIDynamicItemBehavior alloc] initWithItems:#[passwordTextField]];
CGPoint anchor = passwordTextField.center;
anchor.y -= 200;
_attachment = [[UIAttachmentBehavior alloc] initWithItem:passwordTextField
attachedToAnchor:anchor];
_gravity = [[UIGravityBehavior alloc] initWithItems:#[passwordTextField]];
_gravity.magnitude = 10;
[behavior addChildBehavior:_gravity];
[behavior addChildBehavior:_attachment];
[_animator addBehavior:behavior];
The attachment doesn't change the position of the item. Instead it only makes the distance between the item and the attachment point to be fixed.
If you wish to modify the distance then try UISnapBehavior.