UIView锚点->拖动->旋转变换导致闪烁

Posted

技术标签:

【中文标题】UIView锚点->拖动->旋转变换导致闪烁【英文标题】:UIView anchorPoint->drag->rotate transform causes flickering 【发布时间】:2013-06-19 10:18:26 【问题描述】:

目标

我正在尝试复制 UI Dynamics 拉动动画/行为。我在UIView 上设置了一个UIPanGestureRecognizer 用于拖动它。发生拖动时,我将触摸点设置为UIView.layer.anchorPoint 以允许围绕触摸点旋转。在拖动过程中,我检查触摸速度方向并顺时针或逆时针应用CGAffineTransformRotate。这就是我所说的。

问题

动画不流畅,UIView 在拖动和应用旋转时闪烁(在短实例中出现多条视图痕迹)。视频录制为 30fps,因此看不到闪烁。我希望我的解释清楚。

我的视图控制器中的源代码如下。我正在 ios5.0+ 上尝试这个

- (void)viewDidLoad

    [super viewDidLoad];
    // Do any additional setup after loading the view, typically from a nib.
    UIView *animationView = [[UIView alloc] initWithFrame:CGRectMake(50, 50, 100, 100)];
    animationView.layer.backgroundColor = [UIColor redColor].CGColor;
    animationView.layer.shouldRasterize = YES; // Does not help :(
    animationView.autoresizingMask = UIViewAutoresizingNone; // Does not help :(

    [self.view addSubview:animationView];

    UIPanGestureRecognizer *pan = [[UIPanGestureRecognizer alloc] initWithTarget:self action:@selector(pan:)];
    [animationView addGestureRecognizer:pan];
    [animationView release];
    [pan release];


- (void) pan:(UIPanGestureRecognizer*) gesture 

    if(gesture.state==UIGestureRecognizerStateBegan) 
        CGPoint anchor = [gesture locationInView:gesture.view];

        gesture.view.layer.anchorPoint = CGPointMake(anchor.x/gesture.view.bounds.size.width,
                                                     anchor.y/gesture.view.bounds.size.height);

        gesture.view.center = CGPointMake(anchor.x/gesture.view.bounds.size.width,
                                          anchor.y/gesture.view.bounds.size.height);
    
    else if(gesture.state==UIGestureRecognizerStateChanged) 
        CGPoint point1 = [gesture locationInView:self.view];
        gesture.view.center=point1;

        CGPoint velocity = [gesture velocityInView:gesture.view];
        if(velocity.x > 0)
        
            // Dragged right
            if(velocity.y > 0) 
                // Dragged down
                // counter clock
                CGFloat angle = -0.2;
                gesture.view.transform = CGAffineTransformRotate(gesture.view.transform, angle);
            
            else 
                // Dragged up
                // clock
                CGFloat angle = 0.2;
                gesture.view.transform = CGAffineTransformRotate(gesture.view.transform, angle);
            
        
        else
        
            // Dragged left
            if(velocity.y > 0) 
                // Dragged down
                // clock
                CGFloat angle = 0.2;
                gesture.view.transform = CGAffineTransformRotate(gesture.view.transform, angle);
            
            else 
                // Dragged up
                // counter clock
                CGFloat angle = -0.2;
                gesture.view.transform = CGAffineTransformRotate(gesture.view.transform, angle);
            
        
    

我尝试将CATransform3DMakeRotation (angle, 0.0f, 0.0f, 1.0f) 应用于图层而不是视图CGAffineTransformRotate。但是闪烁仍然发生。有什么想法为什么会出现这个问题?我欢迎任何其他方法的建议,以达到相同的结果。

【问题讨论】:

我能够解决这个问题。请参阅下面的答案。 为什么要投反对票?有人不高兴? 【参考方案1】:

在解决这个问题经过认真思考后,我意识到这是一个微不足道的错误。闪烁是因为我正在旋转以使拖动方向发生最轻微的变化。为了解决这个问题,我在进行旋转之前假设了像素变化的任意余量。

解决问题的修改代码如下

- (void) pan:(UIPanGestureRecognizer*) gesture 

    if(gesture.state==UIGestureRecognizerStateBegan) 
        CGPoint anchor = [gesture locationInView:gesture.view];
        gesture.view.layer.anchorPoint = CGPointMake(anchor.x/gesture.view.bounds.size.width,
                                                     anchor.y/gesture.view.bounds.size.height);
    
    else if(gesture.state==UIGestureRecognizerStateChanged) 
        CGPoint point1 = [gesture locationInView:self.view];
        gesture.view.layer.position=point1;

        CGPoint velocity = [gesture velocityInView:gesture.view];
        CGFloat angle = 0.0;
        if(velocity.x > self.view.bounds.size.width*0.15)
        
            // Dragged right
            if(velocity.y > self.view.bounds.size.height*0.02) 
                // Dragged down
                // counter clock
                angle = -0.05;
            
            else if (velocity.y < -self.view.bounds.size.height*0.02) 
                // Dragged up
                // clock
                angle = 0.05;
            
        
        else if (velocity.x < -self.view.bounds.size.width*0.15)
        
            // Dragged left
            if(velocity.y > self.view.bounds.size.height*0.02) 
                // Dragged down
                // clock
                angle = 0.05;
            
            else if (velocity.y < -self.view.bounds.size.height*0.02) 
                // Dragged up
                // counter clock
                angle = -0.05;
            
        
        [self applyRotationToView:gesture.view byAngle:angle];
    


- (void) applyRotationToView:(UIView*)rotationView byAngle:(CGFloat)angle 
    rotationView.transform = CGAffineTransformRotate(rotationView.transform, angle);

动画仍然没有人们想象的那么流畅,但是我已经设法减少了烦人的闪烁。

【讨论】:

以上是关于UIView锚点->拖动->旋转变换导致闪烁的主要内容,如果未能解决你的问题,请参考以下文章

使用 Transform 旋转时调整 UIView 的大小

检测 CGAffineTransformed 视图是不是超出屏幕/UIView 的范围

Swift + Autolayout:旋转 UILabel,保持在 UIView 旁边

旋转 UIView 导致它改变位置

UIView 转换后拖动行为错误

使用变换属性的旋转和动画属性实现大风车效果