繁体   English   中英

UIKit Dynamics的性能问题

[英]Performance Issue With UIKit Dynamics

最近,当我和Stuart Hall的UIKit Dynamics教程( http://stuartkhall.com/posts/flipcase-bounce-in-uikit-dynamics )一起玩时,我发现存在性能问题。

在我向动画师添加了大约50个项目(弹跳球)之后,应用程序变得非常慢 - 几乎被冻结了。 分析显示[UIDynamicAnimator _animatorStep]占用CPU的96%。

有没有人知道如何提高具有大量UIDynamicItems的UIKit Dynamics应用程序的性能?

您可以下载我的代码并亲自查看性能问题:

https://www.dropbox.com/s/zy7ajj6molxm9up/Flipper-UIKit-Dynamics-Poor-Performance.zip

以下是一切发生的代码:

#import "ViewController.h"
#import <CoreMotion/CoreMotion.h>

@interface ViewController () {
    CMMotionManager *_motionManager;
    NSOperationQueue *_queue;

    int _count;
}

@property(nonatomic, strong) UIDynamicAnimator *animator;
@property(nonatomic, strong) UIGravityBehavior *gravityBehavior;
@property(nonatomic, strong) UICollisionBehavior *collisionBehavior;
@property(nonatomic, strong) UIDynamicItemBehavior *bounceBehaviour;

@end

@implementation ViewController

static NSInteger const kBallSize = 25;

- (void)startMotion {
    if (_motionManager == nil) {
        _queue = [[NSOperationQueue alloc] init];
        _motionManager = [[CMMotionManager alloc] init];
        _motionManager.deviceMotionUpdateInterval = 0.1;
    }
    [_motionManager startDeviceMotionUpdatesToQueue:_queue withHandler:^(CMDeviceMotion *motion, NSError *error) {
//        angleLabel.text = [NSString stringWithFormat:@"%g", motion.attitude.pitch];
        self.gravityBehavior.angle = atan2(motion.gravity.x + motion.userAcceleration.x, motion.gravity.y + motion.userAcceleration.y); // motion.attitude.pitch + M_PI / 2.0;
    }];
}

- (void)stopMotion {
    if (_motionManager) {
        [_motionManager stopDeviceMotionUpdates];
    }

    _motionManager = nil;
}

- (void)viewDidLoad {
    [super viewDidLoad];

    [self startMotion];

    // Simple tap gesture
    UITapGestureRecognizer *tapGesture = [[UITapGestureRecognizer alloc] initWithTarget:self action:@selector(onTap:)];
    [self.view addGestureRecognizer:tapGesture];

    // Create our animator, we retain this ourselves
    self.animator = [[UIDynamicAnimator alloc] initWithReferenceView:self.view];

    // Gravity
    self.gravityBehavior = [[UIGravityBehavior alloc] initWithItems:@[]];
    self.gravityBehavior.magnitude = 10;
    [self.animator addBehavior:self.gravityBehavior];

    // Collision - make a fake platform
    self.collisionBehavior = [[UICollisionBehavior alloc] initWithItems:@[]];

    UIBezierPath *aPath = [UIBezierPath bezierPathWithArcCenter:CGPointMake(284, 160)
                                                         radius:160
                                                     startAngle:(CGFloat) (M_PI * 0.0)
                                                       endAngle:(CGFloat) (M_PI * 2)
                                                      clockwise:YES];

    [self.collisionBehavior addBoundaryWithIdentifier:@"bottom" forPath:aPath];
    self.collisionBehavior.translatesReferenceBoundsIntoBoundary = YES;
    [self.animator addBehavior:self.collisionBehavior];

    CAShapeLayer *layer = [CAShapeLayer layer];
    layer.path = aPath.CGPath;
    layer.fillColor = [UIColor lightGrayColor].CGColor;

    [self.view.layer addSublayer:layer];

    // Bounce!
    self.bounceBehaviour = [[UIDynamicItemBehavior alloc] initWithItems:@[]];
    self.bounceBehaviour.elasticity = 0.75;
    self.bounceBehaviour.resistance = 0.1;
    self.bounceBehaviour.friction = 0.01;
    [self.animator addBehavior:self.bounceBehaviour];
}

- (void)didReceiveMemoryWarning {
    [super didReceiveMemoryWarning];
}

- (BOOL)prefersStatusBarHidden {
    return YES;
}

#pragma mark - Gesture Recognizer

- (void)onTap:(UITapGestureRecognizer *)gesture {
    if (gesture.state == UIGestureRecognizerStateEnded) {
        // Grab the x of the touch for the center of our ball
        // Ignore the y, we'll drop from the top
        CGPoint pt = [gesture locationInView:self.view];
        [self dropBallAtX:pt];
    }
}

#pragma mark - Helpers

- (void)dropBallAtX:(CGPoint)p {
    _count++;
    self.label.text = [NSString stringWithFormat:@"Balls: %d", _count];

    // Create a ball and add it to our view
    UIView *ball = [[UIView alloc] initWithFrame:CGRectMake(p.x - (kBallSize / 2), p.y - (kBallSize / 2), kBallSize, kBallSize)];
    CGFloat hue = (arc4random() % 256 / 256.0);               //  0.0 to 1.0
    CGFloat saturation = (arc4random() % 64 / 256.0) + 0.75;  //  0.5 to 1.0, away from white
    CGFloat brightness = (arc4random() % 64 / 256.0) + 0.75;
    ball.backgroundColor = [UIColor colorWithHue:hue saturation:saturation brightness:brightness alpha:1.0];
    ball.layer.cornerRadius = kBallSize / 2;
    ball.layer.masksToBounds = YES;
    [self.view addSubview:ball];

    // Add some gravity
    [self.gravityBehavior addItem:ball];

    // Add the collision
    [self.collisionBehavior addItem:ball];

    // Add the bounce
    [self.bounceBehaviour addItem:ball];
}

@end

这对我来说是一个非常毛茸茸的问题,每个球都会影响它的邻居,这会反过来影响他们的邻居等等。我不会感到惊讶的是,当你添加更多时,非线性复杂性问题会遭受相当严重的性能下降球。 当你接近30-40个球时(在iPhone 5上),我发现fps开始显着下降,就像你说的那样,当你接近50个球时,它很快接近1-2 fps。 如果你让它达到静止(如果你把CMMotionManager放在混合中就不太可能),几秒后fps就恢复了,但是一旦你丢下另一个球或CMMotionManager再次改变引力,问题的复杂性很快就会带来fps再次屈膝。

简单的修复(如增加摩擦,减少弹跳,引入角度阻力,移动到矩形形状,通过添加附件行为(如果使用CMMotionManager ,无论如何都没有意义)将物品固定在一个点上,等等)似乎要有适度的影响力。 令我感到震惊的是,解决这个问题的唯一方法就是放弃UIKit Dynamics以获得一些其他方法,这些方法可以根据您的具体问题做出一些简化的假设(例如,因为您正在处理角度对称的圆形对象,您可以消除旋转复杂性,你可以采用更简单的碰撞逻辑,调整一些上述变量,以便更快地抑制行为;等等)。 我不熟悉任何可以为你做这些的框架,因此这可能需要一些非常重要的代码。 即使你这样做了,你仍然在处理非线性复杂性的问题,性能在某些时候可能仍然会降低。

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

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