#ifndef ZwMatrix_h #define ZwMatrix_h #import "ZwPoint.h" struct XLMatrix { NSInteger column;//列 NSInteger row;//行 CGFloat matrix[4][4];//二维数组,存储行列信息,(row,column)几行几列 }; typedef struct XLMatrix XLMatrix; //定义了一个函数(方法),类型是XLMatrix 类型名是XLMatrix,该结构体有两个参数:行row和列column static XLMatrix XLMatrixMake(NSInteger column, NSInteger row) { XLMatrix matrix; matrix.column = column; matrix.row = row; for(NSInteger i = 0; i < column; i++){ for(NSInteger j = 0; j < row; j++){ matrix.matrix[i][j] = 0;//二维数组,存储行列信息,(row,column)几行几列 } } return matrix; } //定义了一个函数(方法),类型是XLMatrix 类型名是XLMatrixMakeFromArray,该结构体有三个个参数:行row和列column和数据data(PS:这是个指针) #warning 这个方法只是规定了指针的指向,没有改变matrix的值 static XLMatrix XLMatrixMakeFromArray(NSInteger column, NSInteger row, CGFloat *data) { XLMatrix matrix = XLMatrixMake(column, row); for (int i = 0; i < column; i ++) { CGFloat *t = data + (i * row); for (int j = 0; j < row; j++) { matrix.matrix[i][j] = *(t + j); } } return matrix; } //定义了一个函数(方法),类型是XLMatrix 类型名是XLMatrixMutiply,该结构体有两个参数:XLMatrix a和XLMatrix b static XLMatrix XLMatrixMutiply(XLMatrix a, XLMatrix b) { //result的坐标由a的列和b的行组成(列值,行值) XLMatrix result = XLMatrixMake(a.column, b.row); for(NSInteger i = 0; i < a.column; i ++){ for(NSInteger j = 0; j < b.row; j ++){ for(NSInteger k = 0; k < a.row; k++){ result.matrix[i][j] += a.matrix[i][k] * b.matrix[k][j]; } } } return result; } //定义了一个方法,类型是XLPoint, 类型名XLPointMakeRotation,自定义的方法包含三个参数(位置,方向,角度) static XLPoint XLPointMakeRotation(XLPoint point, XLPoint direction, CGFloat angle) { if (angle == 0) { return point; } CGFloat temp2[1][4] = {point.x, point.y, point.z, 1}; XLMatrix result = XLMatrixMakeFromArray(1, 4, *temp2); //如果水平面存在的话,执行判断 if (direction.z * direction.z + direction.y * direction.y != 0) { CGFloat cos1 = direction.z / sqrt(direction.z * direction.z + direction.y * direction.y); CGFloat sin1 = direction.y / sqrt(direction.z * direction.z + direction.y * direction.y); CGFloat t1[4][4] = {{1, 0, 0, 0}, {0, cos1, sin1, 0}, {0, -sin1, cos1, 0}, {0, 0, 0, 1}}; XLMatrix m1 = XLMatrixMakeFromArray(4, 4, *t1); result = XLMatrixMutiply(result, m1); } //如果有值,执行判断 if (direction.x * direction.x + direction.y * direction.y + direction.z * direction.z != 0) { CGFloat cos2 = sqrt(direction.y * direction.y + direction.z * direction.z) / sqrt(direction.x * direction.x + direction.y * direction.y + direction.z * direction.z); CGFloat sin2 = -direction.x / sqrt(direction.x * direction.x + direction.y * direction.y + direction.z * direction.z); CGFloat t2[4][4] = {{cos2, 0, -sin2, 0}, {0, 1, 0, 0}, {sin2, 0, cos2, 0}, {0, 0, 0, 1}}; XLMatrix m2 = XLMatrixMakeFromArray(4, 4, *t2); result = XLMatrixMutiply(result, m2); } CGFloat cos3 = cos(angle); CGFloat sin3 = sin(angle); CGFloat t3[4][4] = {{cos3, sin3, 0, 0}, {-sin3, cos3, 0, 0}, {0, 0, 1, 0}, {0, 0, 0, 1}}; XLMatrix m3 = XLMatrixMakeFromArray(4, 4, *t3); result = XLMatrixMutiply(result, m3); if (direction.x * direction.x + direction.y * direction.y + direction.z * direction.z != 0) { CGFloat cos2 = sqrt(direction.y * direction.y + direction.z * direction.z) / sqrt(direction.x * direction.x + direction.y * direction.y + direction.z * direction.z); CGFloat sin2 = -direction.x / sqrt(direction.x * direction.x + direction.y * direction.y + direction.z * direction.z); CGFloat t2_[4][4] = {{cos2, 0, sin2, 0}, {0, 1, 0, 0}, {-sin2, 0, cos2, 0}, {0, 0, 0, 1}}; XLMatrix m2_ = XLMatrixMakeFromArray(4, 4, *t2_); result = XLMatrixMutiply(result, m2_); } //如果竖直平面有值的话,执行判断 if (direction.z * direction.z + direction.y * direction.y != 0) { CGFloat cos1 = direction.z / sqrt(direction.z * direction.z + direction.y * direction.y); CGFloat sin1 = direction.y / sqrt(direction.z * direction.z + direction.y * direction.y); CGFloat t1_[4][4] = {{1, 0, 0, 0}, {0, cos1, -sin1, 0}, {0, sin1, cos1, 0}, {0, 0, 0, 1}}; XLMatrix m1_ = XLMatrixMakeFromArray(4, 4, *t1_); result = XLMatrixMutiply(result, m1_); } XLPoint resultPoint = XLPointMake(result.matrix[0][0], result.matrix[0][1], result.matrix[0][2]); return resultPoint; } #endif /* ZwMatrix_h */
#ifndef ZwPoint_h #define ZwPoint_h struct XLPoint { CGFloat x; CGFloat y; CGFloat z; }; typedef struct XLPoint XLPoint; XLPoint XLPointMake(CGFloat x, CGFloat y, CGFloat z) { XLPoint point; point.x = x; point.y = y; point.z = z; return point; } #endif /* ZwPoint_h */
#import <UIKit/UIKit.h> @interface ZWSphereView : UIView @property(nonatomic,assign) BOOL isTimerStart; - (void)setItems:(NSArray *)items; - (void)timerStart; - (void)timerStop; @end
#import "ZWSphereView.h" #import "ZwMatrix.h" @interface ZWSphereView()<UIGestureRecognizerDelegate> { NSMutableArray *tags; NSMutableArray *coordinate;//经纬度的坐标 XLPoint normalDirection; CGPoint last; CGFloat velocity; CADisplayLink *timer; CADisplayLink *inertia; } @end @implementation ZWSphereView - (id)initWithFrame:(CGRect)frame { self = [super initWithFrame:frame]; if (self) { //创建一个平移手势 UIPanGestureRecognizer *gesture = [[UIPanGestureRecognizer alloc] initWithTarget:self action:@selector(handlePanGesture:)]; [self addGestureRecognizer:gesture]; } return self; } #pragma mark - initial set - (void)setItems:(NSArray *)items {//tags , coordinate , items 都是数组 tags = [NSMutableArray arrayWithArray:items]; coordinate = [[NSMutableArray alloc] initWithCapacity:0]; for (NSInteger i = 0; i < tags.count; i ++) { UIView *view = [tags objectAtIndex:i]; view.center = CGPointMake(self.frame.size.width / 2., self.frame.size.height / 2.); } CGFloat p1 = M_PI * (3 - sqrt(5)); CGFloat p2 = 2. / tags.count; for (NSInteger i = 0; i < tags.count; i ++) { CGFloat y = i * p2 - 1 + (p2 / 2); CGFloat r = sqrt(1 - y * y); CGFloat p3 = i * p1; CGFloat x = cos(p3) * r; CGFloat z = sin(p3) * r; XLPoint point = XLPointMake(x, y, z); NSValue *value = [NSValue value:&point withObjCType:@encode(XLPoint)]; [coordinate addObject:value]; CGFloat time = (arc4random() % 10 + 10.) / 20.; [UIView animateWithDuration:time delay:0. options:UIViewAnimationOptionCurveEaseOut animations:^{ [self setTagOfPoint:point andIndex:i]; } completion:^(BOOL finished) { }]; } NSInteger a = arc4random() % 10 - 5; NSInteger b = arc4random() % 10 - 5; normalDirection = XLPointMake(a, b, 0); [self timerStart]; } #pragma mark - set frame of point - (void)updateFrameOfPoint:(NSInteger)index direction:(XLPoint)direction andAngle:(CGFloat)angle { NSValue *value = [coordinate objectAtIndex:index]; XLPoint point; [value getValue:&point]; XLPoint rPoint = XLPointMakeRotation(point, direction, angle); value = [NSValue value:&rPoint withObjCType:@encode(XLPoint)]; coordinate[index] = value; [self setTagOfPoint:rPoint andIndex:index]; } - (void)setTagOfPoint: (XLPoint)point andIndex:(NSInteger)index { UIView *view = [tags objectAtIndex:index]; view.center = CGPointMake((point.x + 1) * (self.frame.size.width / 2.), (point.y + 1) * self.frame.size.width / 2.); CGFloat transform = (point.z + 2) / 3; view.transform = CGAffineTransformScale(CGAffineTransformIdentity, transform, transform); view.layer.zPosition = transform; view.alpha = transform; if (point.z < 0) { view.userInteractionEnabled = NO; }else { view.userInteractionEnabled = YES; } } #pragma mark - autoTurnRotation - (void)timerStart {/* CADisplayLink是一个能让我们以和屏幕刷新率相同的频率将内容画到屏幕上的定时器。我们在应用中创建一个新的 CADisplayLink 对象,把它添加到一个runloop中,并给它提供一个 target 和selector 在屏幕刷新的时候调用。 */ //不用NSTimer,不流畅 timer = [CADisplayLink displayLinkWithTarget:self selector:@selector(autoTurnRotation)]; [timer addToRunLoop:[NSRunLoop mainRunLoop] forMode:NSDefaultRunLoopMode]; } - (void)timerStop { [timer invalidate]; timer = nil; } - (void)autoTurnRotation { for (NSInteger i = 0; i < tags.count; i ++) { [self updateFrameOfPoint:i direction:normalDirection andAngle:0.002]; } } #pragma mark - inertia - (void)inertiaStart { [self timerStop]; inertia = [CADisplayLink displayLinkWithTarget:self selector:@selector(inertiaStep)]; [inertia addToRunLoop:[NSRunLoop mainRunLoop] forMode:NSDefaultRunLoopMode]; } - (void)inertiaStop { [inertia invalidate]; inertia = nil; [self timerStart]; } - (void)inertiaStep {//如果速度为0 if (velocity <= 0) { [self inertiaStop]; }else { velocity -= 70.; CGFloat angle = velocity / self.frame.size.width * 2. * inertia.duration; for (NSInteger i = 0; i < tags.count; i ++) { [self updateFrameOfPoint:i direction:normalDirection andAngle:angle]; } } } #pragma mark - gesture selector - (void)handlePanGesture:(UIPanGestureRecognizer *)gesture { if (gesture.state == UIGestureRecognizerStateBegan) { last = [gesture locationInView:self]; [self timerStop]; [self inertiaStop]; }else if (gesture.state == UIGestureRecognizerStateChanged) { CGPoint current = [gesture locationInView:self]; XLPoint direction = XLPointMake(last.y - current.y, current.x - last.x, 0); CGFloat distance = sqrt(direction.x * direction.x + direction.y * direction.y); CGFloat angle = distance / (self.frame.size.width / 2.); for (NSInteger i = 0; i < tags.count; i ++) { [self updateFrameOfPoint:i direction:direction andAngle:angle]; } normalDirection = direction; last = current; }else if (gesture.state == UIGestureRecognizerStateEnded) { CGPoint velocityP = [gesture velocityInView:self]; velocity = sqrt(velocityP.x * velocityP.x + velocityP.y * velocityP.y); [self inertiaStart]; } } @end
调用:
#import "ViewController.h" #import "ZWSphereView.h" @interface ViewController () @property (nonatomic,strong) ZWSphereView *sphereView; @end @implementation ViewController - (void)viewDidLoad { [super viewDidLoad]; self.view.backgroundColor = [UIColor lightGrayColor]; //球体的宽和高 CGFloat sphereViewW = self.view.frame.size.width - 30 * 2; CGFloat sphereViewH = sphereViewW; //调用自定义的类创建一个球 的对象 _sphereView = [[ZWSphereView alloc] initWithFrame:CGRectMake(30, 100, sphereViewW, sphereViewH)]; NSMutableArray *array = [[NSMutableArray alloc] initWithCapacity:0]; for (NSInteger i = 0; i < 10; i ++) { UIButton *btn = [UIButton buttonWithType:UIButtonTypeSystem]; [btn setTitle:[NSString stringWithFormat:@"生活大爆炸%ld", i] forState:UIControlStateNormal]; //设置按钮的参数 // btn.backgroundColor = [UIColor colorWithRed:arc4random_uniform(255)/255. green:arc4random_uniform(255)/255. blue:arc4random_uniform(255)/255. alpha:1.]; btn.backgroundColor = [UIColor clearColor]; //button的字体颜色 [btn setTitleColor:[UIColor whiteColor] forState:UIControlStateNormal]; btn.titleLabel.font = [UIFont systemFontOfSize:15]; btn.frame = CGRectMake(0, 0, 100, 30); btn.layer.cornerRadius = 3; btn.clipsToBounds = YES; [btn addTarget:self action:@selector(buttonPressed:) forControlEvents:UIControlEventTouchUpInside]; [array addObject:btn]; [_sphereView addSubview:btn]; } [_sphereView setItems:array]; [self.view addSubview:_sphereView]; } - (void)buttonPressed:(UIButton *)btn { NSLog(@",,, = %@", [btn currentTitle]); [_sphereView timerStop]; [UIView animateWithDuration:0.25 animations:^{ btn.transform = CGAffineTransformMakeScale(2., 2.); } completion:^(BOOL finished) { [UIView animateWithDuration:0.25 animations:^{ btn.transform = CGAffineTransformMakeScale(1., 1.); } completion:^(BOOL finished) { [_sphereView timerStart]; }]; }]; } @end