【无限互联】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>


    另外,菜单push到下一个界面的具体实现有一个pushViewController:的方法给大家参考,具体里面的实现可以根据自己的需要进行设计









0 0
原创粉丝点击