转圈菜单栏的实现《一》

再过几天就要放假咯,前两天也是够闹心的了,上线被苹果拒了好几次,心都拔凉拔凉的了,好吧,废话说完了,下面直接入主题:

Demo地址

https://github.com/wxh794708907/YJYYCircleMenu.git

效果图
YJYYCircleMenu.gif

效果图大概就是这样子,这其实也是公司项目中的一个需求,这里我单独拿出来讲

需求分析:

1 .首先这是一个菜单,这个菜单包含了5个元素,暂且叫它5个item吧,其实真正实现的时候是用的按钮,
2 .它是有动画的,一直在绕着中心来旋转,而且转动一会后会短暂的停大概1s的时间
3 .它其实是有点击事件的(但是这篇文章我先不讲,下篇再拿来说)

具体实现

1.UI实现,5个按钮围绕中心点进行布局,这个时候你可能会想,按钮的frame该怎么去设置,其实刚开始做的时候我也一直在疑惑,我到底该怎么去布局,怎么去设置frame,后来茅塞顿开,其实根本就不需要考虑frame,只需要考虑宽高就行,具体待会你看代码就明白了。
2.动画分析:可能每个人都有自己的思路去实现这个动画,有些人可能想的是将所有的6个按钮都布局好之后 通过给整个菜单来添加关键帧动画, 而我的思路是通过给每一个按钮去添加一个动画来达到效果,这也就是为什么我不需要考虑x和y的原因,因为动画是基于layer来做的。
3.点击事件暂且不在本篇文章中来说 敬请期待下篇。

代码实践

控制器中代码:

//
//  ViewController.m
//  YJYYCircleMenu
//
//  Created by 遇见远洋 on 17/1/2.
//  Copyright © 2017年 遇见远洋. All rights reserved.
//

#import "ViewController.h"
#import "YJYYCycleMenu.h"

@interface ViewController ()

@end

@implementation ViewController

- (void)viewDidLoad {
    [super viewDidLoad];
    YJYYCycleMenu * menu = [YJYYCycleMenu cycleMenuWithTitles:@[@"读新闻",@"导航",@"订咖啡",@"查资讯",@"萌萌哒"] menuWidth:60 center:self.view.center radius:100];
    [self.view addSubview:menu];
}


- (void)didReceiveMemoryWarning {
    [super didReceiveMemoryWarning];
    // Dispose of any resources that can be recreated.
}

@end

.h:

//
//  YJYYCycleMenu.h
//  YJYYCircleMenu
//
//  Created by 遇见远洋 on 17/1/2.
//  Copyright © 2017年 遇见远洋. All rights reserved.
//

#import <UIKit/UIKit.h>

@interface YJYYCycleMenu : UIView


/**
 快速实例化菜单

 @param titles 文本数组 数组个数多少就是多少个menu
 @param menuWidth 菜单item的高度 最好宽高相等
 @param center 中心点 围绕那个点抓圈
 @param radius 半径 选装半径
 */
+ (instancetype)cycleMenuWithTitles:(NSArray<NSString *> *)titles menuWidth:(CGFloat)menuWidth center:(CGPoint)center radius:(CGFloat)radius;

@end

.m

//
//  YJYYCycleMenu.m
//  YJYYCircleMenu
//
//  Created by 遇见远洋 on 17/1/2.
//  Copyright © 2017年 遇见远洋. All rights reserved.
//

#import "YJYYCycleMenu.h"

@interface YJYYCycleMenu ()
/**<按钮数组*/
@property (strong,nonatomic)NSMutableArray *btnsArray;
/**<标题数组*/
@property (strong,nonatomic)NSArray *titiles;
/**<开始角度*/
@property (strong,nonatomic)NSMutableArray *startAngle;
/**<结束角度*/
@property (strong,nonatomic)NSMutableArray *endAngle;
/** 半径 */
@property(nonatomic,assign) CGFloat radius;
/** 中心点 */
@property(nonatomic,assign) CGPoint centerPoint;
/** 按钮宽高 */
@property(nonatomic,assign) CGFloat itemHW;
@end

@implementation YJYYCycleMenu

+ (instancetype)cycleMenuWithTitles:(NSArray<NSString *> *)titles menuWidth:(CGFloat)menuWidth center:(CGPoint)center radius:(CGFloat)radius {
    YJYYCycleMenu * menu = [[YJYYCycleMenu alloc]init];
    menu.titiles = titles;
    menu.radius = radius;
    menu.centerPoint = center;
    menu.itemHW = menuWidth;
    [menu startAnimation];
    return menu;
}

- (NSMutableArray *)btnsArray {
    if (!_btnsArray) {
        _btnsArray = [NSMutableArray arrayWithCapacity:self.titiles.count];
        for (int i = 0; i < self.titiles.count; i++) {
            UIButton * circleBtn  = [[UIButton alloc]initWithFrame:CGRectMake(0, 0, _itemHW, _itemHW)];
            [circleBtn setTitle:self.titiles[i] forState:UIControlStateNormal];
            circleBtn.backgroundColor = [UIColor colorWithWhite:0 alpha:0.3f];
            circleBtn.layer.cornerRadius = _itemHW*0.5;
            circleBtn.layer.masksToBounds = YES;
            [circleBtn setTitleColor:[UIColor whiteColor] forState:UIControlStateNormal];
            circleBtn.titleLabel.font = [UIFont systemFontOfSize:14];
            [self addSubview:circleBtn];
            [_btnsArray addObject:circleBtn];
        }
    }
    return _btnsArray;
}

- (NSArray *)titiles {
    if (!_titiles) {
        _titiles = [NSArray array];
    }
    return _titiles;
}

- (NSMutableArray *)startAngle {
    if (!_startAngle) {
        _startAngle = [NSMutableArray array];
        for (int i = 0; i<self.titiles.count;i++ ) {
            [_startAngle addObject:@(2*M_PI/self.titiles.count*i)];
        }
        
        NSLog(@"%@",_startAngle);
    }
    return _startAngle;
}


- (NSMutableArray *)endAngle {
    if (!_endAngle) {
        _endAngle = [NSMutableArray array];
        for (int i = 0; i<self.titiles.count;i++ ) {
            CGFloat angle = 2*M_PI - (2*M_PI/self.titiles.count*i);
            if (angle + [self.startAngle[i] floatValue] != 2*M_PI) {
                
                angle = -angle;
                
                NSLog(@"%f========%f",[self.startAngle[i] floatValue],angle);
            }
            
            [_endAngle addObject:@(angle)];
        }
    }
    return _endAngle;
}


#pragma  mark -  事件处理
#pragma  mark -
/**
 *  开始转圈动画
 */
- (void)startAnimation {
    [self.btnsArray enumerateObjectsUsingBlock:^(UIButton  * circleBtn, NSUInteger idx, BOOL * _Nonnull stop) {
        NSLog(@"%@",circleBtn.currentTitle);
        [self keyFrameWithStart:[self.startAngle[idx] floatValue] endAngle:[self.endAngle[idx] floatValue] animationView:circleBtn];
    }];
}


/**
 *  帧动画封装
 *
 *  @param startAngle    开始角度
 *  @param endAngle      结束角度
 *  @param animationView 动画view
 */
- (void)keyFrameWithStart:(CGFloat)startAngle endAngle:(CGFloat)endAngle animationView:(UIView *)animationView{
    CAKeyframeAnimation * keyFrame = [CAKeyframeAnimation animationWithKeyPath:@"position"];
    
    //创建一条路径
    UIBezierPath * bezierPath = [UIBezierPath bezierPathWithArcCenter:self.centerPoint radius:self.radius startAngle:startAngle endAngle:endAngle clockwise:YES];
    keyFrame.path = bezierPath.CGPath;
    //1.2设置动画执行完毕后,不删除动画
    keyFrame.removedOnCompletion=NO;
    //1.3设置保存动画的最新状态
    keyFrame.fillMode=kCAFillModeForwards;
    //1.4设置动画执行的时间
    keyFrame.duration=15.0;
    keyFrame.repeatCount = NSIntegerMax;
    //1.5设置动画的节奏
    keyFrame.timingFunction=[CAMediaTimingFunction functionWithName:kCAMediaTimingFunctionEaseInEaseOut];
    //2.添加核心动画
    [animationView.layer addAnimation:keyFrame forKey:nil];
}

@end

总结分析

应该会有不少人可能没那么明白原理,为什么没有设置按钮的X和Y,出来就可以让按钮围绕中心点布局好了,其实你只需要关注下面的这个方法就好了,这个方式是核心:

- (void)keyFrameWithStart:(CGFloat)startAngle endAngle:(CGFloat)endAngle animationView:(UIView *)animationView;

由于动画是基于layer的,所以你只要有宽高,开始动画的时候设置好开始角度和结束角度,这样就可以达到我们的需求了,这里再说明一下的是,其实角度和结束角度我是怎么考虑的,这里我也是考虑了好久的地方,但是现在还是可能会有问题的,所以你如果需要运用到实际项目中的时候 如果出现问题的话,一般都是因为结束角度没有设置对导致的

角度计算

1.起始角度是由按钮个数来决定的,最主要是计算没个按钮之间的角度差 通过" 2*M_PI/self.titiles.count * i "这个来计算角度差值,
2.结束角度
就比较麻烦了,如果单纯用2π - 起始角度会出现问题,还需要考虑角度负数的问题,具体你可以看下endAngle的懒加载。

下篇预告

基本到这也就差不多实现了,可能我的思路比较low,大神们不喜勿喷,有什么好的实现思路 欢迎探讨 嘎嘎.... 下篇文章就单独来讲按钮的点击事件了,今天就写到这了,下篇再见哦..

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 159,117评论 4 362
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 67,328评论 1 293
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 108,839评论 0 243
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 44,007评论 0 206
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 52,384评论 3 287
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 40,629评论 1 219
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 31,880评论 2 313
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 30,593评论 0 198
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 34,313评论 1 243
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 30,575评论 2 246
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 32,066评论 1 260
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 28,392评论 2 253
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 33,052评论 3 236
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 26,082评论 0 8
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 26,844评论 0 195
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 35,662评论 2 274
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 35,575评论 2 270

推荐阅读更多精彩内容

  • Android 自定义View的各种姿势1 Activity的显示之ViewRootImpl详解 Activity...
    passiontim阅读 170,569评论 25 707
  • 发现 关注 消息 iOS 第三方库、插件、知名博客总结 作者大灰狼的小绵羊哥哥关注 2017.06.26 09:4...
    肇东周阅读 11,614评论 4 59
  • 在iOS中随处都可以看到绚丽的动画效果,实现这些动画的过程并不复杂,今天将带大家一窥ios动画全貌。在这里你可以看...
    每天刷两次牙阅读 8,321评论 6 30
  • 如果是“工作总结”那么上周可以不用总结了,我基本上没有干什么跟工作相关的事情,这可能是我来北京后最“颓废”的一周了...
    小苤阅读 236评论 0 0