【无限互联】KYCircleMenu 的实现
来源:互联网 发布:大连锐鑫软件 编辑:程序博客网 时间:2024/05/24 00:11
现在的社会越来越讲究个性化的设计,连IOS的菜单设计也不列外,下面介绍的是一款酷炫的菜单的设计。其效果图如下:
关闭状态: 打开状态:
它的实现原理为:
1、首先需要定义一个初始化方法,把菜单的个数、图片、大小等所需要的信息传过来
<span style="font-size:18px;">- (instancetype)initWithButtonCount:(NSInteger)buttonCount menuSize:(CGFloat)menuSize buttonSize:(CGFloat)buttonSize buttonImageNameFormat:(NSString *)buttonImageNameFormat centerButtonSize:(CGFloat)centerButtonSize centerButtonImageName:(NSString *)centerButtonImageName centerButtonBackgroundImageName:(NSString *)centerButtonBackgroundImageName{ if (self = [self init]) { buttonCount_ = buttonCount; menuSize_ = menuSize; buttonSize_ = buttonSize; buttonImageNameFormat_ = buttonImageNameFormat; centerButtonSize_ = centerButtonSize; centerButtonImageName_ = centerButtonImageName; centerButtonBackgroundImageName_ = centerButtonBackgroundImageName; // Defualt value for triangle hypotenuse //三角形斜边Defualt值 就是menuView视图的斜边 就是他的中心点得位置 是相对于menuView的 defaultTriangleHypotenuse_ = (menuSize - buttonSize) * .5f; //按钮反弹后离斜边的最小距离 minBounceOfTriangleHypotenuse_ = defaultTriangleHypotenuse_ - 12.f;</span>
<span style="font-size:18px;"> //按钮反弹后离斜边的最大距离 maxBounceOfTriangleHypotenuse_ = defaultTriangleHypotenuse_ + 12.f; maxTriangleHypotenuse_ = kKYCircleMenuViewHeight * .5f; // Buttons' origin frame CGFloat originX = (menuSize_ - centerButtonSize_) * .5f; //6个按钮最开始统一所在的位置 即中心点的位置 buttonOriginFrame_ = (CGRect){{originX, originX}, {centerButtonSize_, centerButtonSize_}}; } return self;}</span>
2、因为程序刚运行的时候要隐藏导航栏,所以需要复写loadView方法,当导航栏隐藏的时候视图的高度为屏幕的高度,当导航栏显示的时候视图的高度为(屏幕的高度- 导航栏的高度)
<span style="font-size:18px;">- (void)loadView{ CGFloat viewHeight = (self.navigationController.isNavigationBarHidden ? kKYCircleMenuViewHeight : kKYCircleMenuViewHeight - kKYCircleMenuNavigationBarHeight); CGRect frame = CGRectMake(0.f, 0.f, kKYCircleMenuViewWidth, viewHeight); UIView * view = [[UIView alloc] initWithFrame:frame]; self.view = view; self.view.backgroundColor = [UIColor colorWithPatternImage:[UIImage imageNamed:@"backGround.jpg"]];}</span>3、菜单视图的创建、菜单按钮的创建、将不包括中心点按钮以外的其他的菜单按钮放在一个菜单视图上面 、程序开始运行的时候就发送关闭的通知。
<span style="font-size:18px;">- (void)viewDidLoad{ [super viewDidLoad]; CGFloat viewHeight = CGRectGetHeight(self.view.frame); CGFloat viewWidth = CGRectGetWidth(self.view.frame); //菜单视图的创建 CGRect centerMenuFrame = CGRectMake((viewWidth - menuSize_) * .5f, (viewHeight - menuSize_) * .5f, menuSize_, menuSize_); menu_ = [[UIView alloc] initWithFrame:centerMenuFrame]; [menu_ setAlpha:0.f]; [self.view addSubview:menu_]; </span>
<span style="font-size:18px;"> //菜单按钮的创建 NSString * imageName = nil; for (int i = 1; i <= buttonCount_; ++i) { UIButton * button = [[UIButton alloc] initWithFrame:buttonOriginFrame_]; [button setOpaque:NO]; [button setTag:i]; imageName = [NSString stringWithFormat:self.buttonImageNameFormat, button.tag]; [button setImage:[UIImage imageNamed:imageName] forState:UIControlStateNormal]; [button addTarget:self action:@selector(runButtonActions:) forControlEvents:UIControlEventTouchUpInside]; [self.menu addSubview:button]; } // 中心点按钮 它的frame是基于self.view来计算的 CGRect mainButtonFrame = CGRectMake((CGRectGetWidth(self.view.frame) - centerButtonSize_) * .5f, (CGRectGetHeight(self.view.frame) - centerButtonSize_) * .5f, centerButtonSize_, centerButtonSize_); centerButton_ = [[UIButton alloc] initWithFrame:mainButtonFrame]; [centerButton_ setBackgroundImage:[UIImage imageNamed:self.centerButtonBackgroundImageName] forState:UIControlStateNormal]; [centerButton_ setImage:[UIImage imageNamed:self.centerButtonImageName] forState:UIControlStateNormal]; [centerButton_ addTarget:self action:@selector(_toggle:) forControlEvents:UIControlEventTouchUpInside]; [self.view addSubview:centerButton_]; //设置通知 [self _setupNotificationObserver];}</span>4.通知的方法
<span style="font-size:18px;">- (void)_setupNotificationObserver{ //为关闭自动添加观察者 //如果取消通知,按中心点按钮 [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(_close:) name:kKYNCircleMenuClose object:nil];}</span>
5.将所有菜单按钮的frame设置为中心点的frame 菜单视图的透明度设为0,即关闭菜单
<span style="font-size:18px;">//隐藏所有按钮关闭菜单<span style="color:#006600;">- (void)_close:(NSNotification *)notification{</span></span>
<span style="font-size:18px;color:#006600;"> if (isClosed_) return; isInProcessing_ = YES; // 隐藏按钮的动画 [UIView animateWithDuration:.3f delay:0.f options:UIViewAnimationCurveEaseIn animations:^{ for (UIButton * button in [self.menu subviews])</span>
<span style="font-size:18px;"><span style="color:#006600;"> //将按钮的frame设置为button刚开始所在的位置 [button setFrame:buttonOriginFrame_]; [self.menu setAlpha:0.f]; } completion:^(BOOL finished) { isClosed_ = YES; isOpening_ = NO; isInProcessing_ = NO; }];}</span></span>6.中心点按钮的点击事件
<span style="font-size:18px;">//循环切换菜单- (void)_toggle:(id)sender{</span>
<span style="font-size:18px;"> //因为菜单一开始是处于关闭的状态,所以点击按钮就会打开菜单 (isClosed_ ? [self open] : [self _close:nil]);}</span>
7、打开菜单(打开菜单时会有一个摇晃的效果,先是离中心点的最大距离最后再恢复到默认的距离,所以会有两个动画效果)
<span style="color:#006600;"><span style="font-size:18px;">- (void)open{ if (isOpening_) return; isInProcessing_ = YES; // 按钮出现时的动画 [UIView animateWithDuration:.3f delay:0.f options:UIViewAnimationCurveEaseInOut animations:^{ [self.menu setAlpha:1.f]; // Compute buttons' frame and set for them, based on |buttonCount| //计算按钮的坐标 [self _updateButtonsLayoutWithTriangleHypotenuse:maxBounceOfTriangleHypotenuse_]; } completion:^(BOOL finished) { [UIView animateWithDuration:.1f delay:0.f options:UIViewAnimationCurveEaseInOut animations:^{ [self _updateButtonsLayoutWithTriangleHypotenuse:defaultTriangleHypotenuse_]; } completion:^(BOOL finished) { isOpening_ = YES; isClosed_ = NO; isInProcessing_ = NO; }]; }];}</span></span>
8、最为重要的就是下面的步骤,也就是菜单按钮坐标的计算(用switch语句来判断菜单的个数,求得它们各自所在的位置)
<span style="color:#006600;"><span style="font-size:18px;">- (void)_updateButtonsLayoutWithTriangleHypotenuse:(CGFloat)triangleHypotenuse{ //三角形按钮的位置的值 // // /| a: triangleA = c * cos(x) // c / | b b: triangleB = c * sin(x) // /)x| c: triangleHypotenuse // ----- x: degree // a // CGFloat centerBallMenuHalfSize = menuSize_ * .5f; //menuView的中心 CGFloat buttonRadius = centerButtonSize_ * .5f; //按钮的中心 if (! triangleHypotenuse) triangleHypotenuse = defaultTriangleHypotenuse_; // Distance to Ball Center // // o o o o o o o o o o o o o // \|/ \|/ \|/ \|/ \|/ \|/ // 1 --|-- 2 --|-- 3 --|-- 4 --|-- 5 --|-- 6 --|-- // /|\ /|\ /|\ /|\ /|\ /|\ // o o o o o o o o // switch (buttonCount_) { case 1: [self _setButtonWithTag:1 origin:CGPointMake(centerBallMenuHalfSize - buttonRadius, centerBallMenuHalfSize - triangleHypotenuse - buttonRadius)]; break; case 2: { CGFloat degree = M_PI / 4.0f; // = 45 * M_PI / 180 CGFloat triangleB = triangleHypotenuse * sinf(degree); CGFloat negativeValue = centerBallMenuHalfSize - triangleB - buttonRadius; CGFloat positiveValue = centerBallMenuHalfSize + triangleB - buttonRadius; [self _setButtonWithTag:1 origin:CGPointMake(negativeValue, negativeValue)]; [self _setButtonWithTag:2 origin:CGPointMake(positiveValue, negativeValue)]; break; } case 3: { // = 360.0f / self.buttonCount * M_PI / 180.0f; // E.g: if |buttonCount_ = 6|, then |degree = 60.0f * M_PI / 180.0f|; // CGFloat degree = 2 * M_PI / self.buttonCount; // CGFloat degree = M_PI / 3.0f; // = 60 * M_PI / 180 CGFloat triangleA = triangleHypotenuse * cosf(degree); CGFloat triangleB = triangleHypotenuse * sinf(degree); [self _setButtonWithTag:1 origin:CGPointMake(centerBallMenuHalfSize - triangleB - buttonRadius,centerBallMenuHalfSize - triangleA - buttonRadius)]; [self _setButtonWithTag:2 origin:CGPointMake(centerBallMenuHalfSize + triangleB- buttonRadius, centerBallMenuHalfSize - triangleA - buttonRadius)]; [self _setButtonWithTag:3 origin:CGPointMake(centerBallMenuHalfSize - buttonRadius, centerBallMenuHalfSize + triangleHypotenuse - buttonRadius)]; break; } case 4: { CGFloat degree = M_PI / 4.0f; // = 45 * M_PI / 180 CGFloat triangleB = triangleHypotenuse * sinf(degree); CGFloat negativeValue = centerBallMenuHalfSize - triangleB - buttonRadius; CGFloat positiveValue = centerBallMenuHalfSize + triangleB - buttonRadius; [self _setButtonWithTag:1 origin:CGPointMake(negativeValue, negativeValue)]; [self _setButtonWithTag:2 origin:CGPointMake(positiveValue, negativeValue)]; [self _setButtonWithTag:3 origin:CGPointMake(negativeValue, positiveValue)]; [self _setButtonWithTag:4 origin:CGPointMake(positiveValue, positiveValue)]; break; } case 5: { CGFloat degree = M_PI / 2.5f; // = 72 * M_PI / 180 CGFloat triangleA = triangleHypotenuse * cosf(degree); CGFloat triangleB = triangleHypotenuse * sinf(degree); [self _setButtonWithTag:1 origin:CGPointMake(centerBallMenuHalfSize - triangleB - buttonRadius, centerBallMenuHalfSize - triangleA - buttonRadius)]; [self _setButtonWithTag:2 origin:CGPointMake(centerBallMenuHalfSize - buttonRadius, centerBallMenuHalfSize - triangleHypotenuse - buttonRadius)]; [self _setButtonWithTag:3 origin:CGPointMake(centerBallMenuHalfSize + triangleB - buttonRadius, centerBallMenuHalfSize - triangleA - buttonRadius)]; degree = M_PI / 5.0f; // = 36 * M_PI / 180 triangleA = triangleHypotenuse * cosf(degree); triangleB = triangleHypotenuse * sinf(degree); [self _setButtonWithTag:4 origin:CGPointMake(centerBallMenuHalfSize - triangleB - buttonRadius, centerBallMenuHalfSize + triangleA - buttonRadius)]; [self _setButtonWithTag:5 origin:CGPointMake(centerBallMenuHalfSize + triangleB - buttonRadius, centerBallMenuHalfSize + triangleA - buttonRadius)]; break; } case 6: { CGFloat degree = M_PI / 3.0f; // = 60 * M_PI / 180 CGFloat triangleA = triangleHypotenuse * cosf(degree); CGFloat triangleB = triangleHypotenuse * sinf(degree); [self _setButtonWithTag:1 origin:CGPointMake(centerBallMenuHalfSize - triangleB - buttonRadius, centerBallMenuHalfSize - triangleA - buttonRadius)]; [self _setButtonWithTag:2 origin:CGPointMake(centerBallMenuHalfSize - buttonRadius, centerBallMenuHalfSize - triangleHypotenuse - buttonRadius)]; [self _setButtonWithTag:3 origin:CGPointMake(centerBallMenuHalfSize + triangleB - buttonRadius, centerBallMenuHalfSize - triangleA - buttonRadius)]; [self _setButtonWithTag:4 origin:CGPointMake(centerBallMenuHalfSize - triangleB - buttonRadius, centerBallMenuHalfSize + triangleA - buttonRadius)]; [self _setButtonWithTag:5 origin:CGPointMake(centerBallMenuHalfSize - buttonRadius, centerBallMenuHalfSize + triangleHypotenuse - buttonRadius)]; [self _setButtonWithTag:6 origin:CGPointMake(centerBallMenuHalfSize + triangleB - buttonRadius, centerBallMenuHalfSize + triangleA - buttonRadius)]; break; } default: break; }}</span></span>9、根据它的tag值,写出它对应的frame
<span style="font-size:18px;">- (void)_setButtonWithTag:(NSInteger)buttonTag origin:(CGPoint)origin{ UIButton * button = (UIButton *)[self.menu viewWithTag:buttonTag]; [button setFrame:CGRectMake(origin.x, origin.y, centerButtonSize_, centerButtonSize_)]; button = nil;}</span>10、当点击菜单按钮时调用的方法
<span style="font-size:18px;">- (void)pushViewController:(id)viewController{</span>
<span style="font-size:18px;"> //push到下一个界面时的动画 [UIView animateWithDuration:.3f delay:0.f options:UIViewAnimationOptionCurveEaseInOut animations:^{ [self _updateButtonsLayoutWithTriangleHypotenuse:maxTriangleHypotenuse_]; [self.menu setAlpha:0.f]; } completion:^(BOOL finished) { [self.navigationController pushViewController:viewController animated:YES]; }];}</span>11、菜单按钮的点击事件方法
<span style="font-size:18px;">- (void)runButtonActions:(id)sender{#ifndef KY_CIRCLEMENU_WITH_NAVIGATIONBAR [self.navigationController setNavigationBarHidden:NO animated:YES];#endif // Close center menu// [self _closeCenterMenuView:nil]; shouldRecoverToNormalStatusWhenViewWillAppear_ = YES;}</span>
12、当视图将要出现时调用的方法
<span style="font-size:18px;">- (void)viewWillAppear:(BOOL)animated{ [super viewWillAppear:animated]; #ifndef KY_CIRCLEMENU_WITH_NAVIGATIONBAR [self.navigationController setNavigationBarHidden:YES animated:YES];#endif // If it is from child view by press the buttons, // recover menu to normal state //如果他的子视图按下按钮,菜单恢复到正常状态 if (shouldRecoverToNormalStatusWhenViewWillAppear_) [self performSelector:@selector(recoverToNormalStatus) withObject:nil afterDelay:.3f];}</span>
13、当我们push到下一个界面,点返回按钮时,我们需要将按钮还原到展开的状态,恢复的时候菜单会有一个摇晃的效果,先是离中心点的最小距离最后再恢复到默认的距离,所以会有两个动画效果
<span style="font-size:18px;">//恢复到正常状态- (void)recoverToNormalStatus{ [self _updateButtonsLayoutWithTriangleHypotenuse:maxTriangleHypotenuse_]; [UIView animateWithDuration:.3f delay:0.f options:UIViewAnimationOptionCurveEaseInOut animations:^{ // Show buttons & slide in to center [self.menu setAlpha:1.f]; [self _updateButtonsLayoutWithTriangleHypotenuse:minBounceOfTriangleHypotenuse_]; } completion:^(BOOL finished) { [UIView animateWithDuration:.1f delay:0.f options:UIViewAnimationOptionCurveEaseInOut animations:^{ [self _updateButtonsLayoutWithTriangleHypotenuse:defaultTriangleHypotenuse_]; } completion:nil]; }];}</span>
0 0
- 【无限互联】KYCircleMenu 的实现
- 【无线互联】IOS开发之KYCircleMenu框架
- 【无限互联】IOS开发之手势密码的实现
- 无限级分类的实现
- 无限级分类的实现
- php 无限分类的实现
- 无限级别菜单的实现
- 无限分类的CURD实现
- 无限级分类的实现
- 无限级分类的实现
- 无限轮播的实现
- 无限轮播的实现
- 无限分类递归的实现
- [无限互联]IOS之MMdrawer框架的使用及实现原理
- 【无限互联】下拉刷新的使用
- 无限互联@彪哥
- 无限分类的ASP实现方法
- 实现基于 Ajax 的无限级菜单
- TCP、UDP详解
- UVA - 10106 Product
- OPENcv从相机采集图像数据-图像处理接口IPLIMAGE结构的指针
- 一个按钮导致的惨案
- static和extern 的区别
- 【无限互联】KYCircleMenu 的实现
- SQL的几种连接:内连接、左联接、右连接、全连接、交叉连接
- UVA489水题
- linux环境下django开发环境的搭建
- File Inclusion -low
- OC点语法和变量作用域
- Asp.net中动态Menu的实践
- Eclipse上修改Jython代码的Comment颜色
- 天气API-----开源免费天气预报接口API以及全国所有地区代码!!(国家气象局提供)