最近,当我在玩 Stuart Hall 的 UIKit Dynamics 教程 ( http://stuartkhall.com/posts/flipcase-bounce-in-uikit-dynamics ) 时,我发现存在性能问题。
在我向动画师添加了大约 50 个项目(弹跳球)后,应用程序变得非常缓慢——几乎冻结了。分析显示 [UIDynamicAnimator _animatorStep] 占用了 96% 的 CPU。
有人知道如何提高具有大量 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