http://code4app.com/ios/51fa7d7e6803fa2710000006

我有个很牛的同学朋友同事舍友···他技术牛人,写的博客都是原创,粉丝无数,说实话我真的挺妒嫉的,试过为了拉取博客人气,专门在各处找来一些没有用的博文,伪装成自己的。However,不是自己的就不是自己的。还是承认比较好,我的博客里面主要还是为了个人学习所用,所以,看到者勿吐槽。我会努力,争取成为牛人,写出属于自己的技术博文。

实现类似Mac OS系统Dock上的弹出菜单(stack menu)。点击主按钮,会弹出一个由成弧形依次排列的多个按钮组成的菜单。适用环境:Xcode 4.5, iOS 5.0 以上。

这个Demo主要是在Code4APP上下载的,绝非我的创作。后面我应该还会不断从上面学习各种Demo,并粘贴到自己的博客中。

具体它提供的也只有代码,那我就从代码开始分析吧,略笨的方法

PCStackMenuItem

 #import <UIKit/UIKit.h>

 @interface PCStackMenuItem : UIView

 - (id)initWithFrame:(CGRect)frame withTitle:(NSString *)title withImage:(UIImage *)image alignment:(UITextAlignment)alignment;

 @property (nonatomic)                BOOL            highlight;

 @property (nonatomic, readonly)        UILabel            *stackTitleLabel;
 @property (nonatomic, readonly)     UIImageView        *stackIimageView;

 @end

 CGAffineTransform CGAffineTransformMakeRotationAt(CGFloat angle, CGPoint pt);

定义了菜单单项视图,一个Label和一个ImageView,所以如果你希望能实现一个更复杂的弹出菜单单项视图,就改这个文件吧。该类中有个BOOL型属性highlight,应该就是判断菜单项是否为当前选中项吧。而这个Demo并没有实现hover特效,所以有机会后面我会在这个基础上修改,添加hover的效果。UIView类型属性hightlightView则是为了在选中的时候添加一个0.2透明度的UIView来实现选择阴影效果的。

 #import "PCStackMenuItem.h"
 #import <QuartzCore/QuartzCore.h>

 @interface PCStackMenuItem ()
 {
     UIView                    *_highlightView;
 }

 @end

 @implementation PCStackMenuItem

 - (BOOL)highlight
 {
     return (_highlightView != nil);
 }

 - (void)setHighlight:(BOOL)highlight
 {
     if(!highlight && _highlightView == nil)
         return;
     if(highlight && _highlightView != nil)
         return;

     if(highlight)
     {
         _highlightView = [[UIView alloc] initWithFrame:self.bounds];
         _highlightView.layer.cornerRadius = ;
         _highlightView.layer.masksToBounds = YES;
         _highlightView.backgroundColor = [[UIColor darkGrayColor] colorWithAlphaComponent:0.2];
         [super addSubview:_highlightView];
     }
     else
     {
         [_highlightView removeFromSuperview];
         _highlightView = nil;
     }
 }

 - (id)initWithFrame:(CGRect)frame withTitle:(NSString *)title withImage:(UIImage *)image alignment:(UITextAlignment)alignment
 {
     self = [self initWithFrame:frame];
     if (self)
     {
         CGRect rect = CGRectMake(alignment == UITextAlignmentLeft ?  : frame.size.width - frame.size.height, , frame.size.height, frame.size.height);
         _stackIimageView = [[UIImageView alloc] initWithFrame:rect];
         _stackIimageView.contentMode = UIViewContentModeScaleAspectFit;
         _stackIimageView.image = image;
         [self addSubview:_stackIimageView];

         rect = CGRectMake(alignment == UITextAlignmentRight ?  : frame.size.height + , , frame.size.width - frame.size.height - , frame.size.height - );
         _stackTitleLabel = [[UILabel alloc] initWithFrame:rect];
         _stackTitleLabel.textAlignment = alignment;
         _stackTitleLabel.font = [UIFont boldSystemFontOfSize:];
         _stackTitleLabel.backgroundColor = [UIColor clearColor];
         _stackTitleLabel.textColor = [UIColor whiteColor];
         _stackTitleLabel.shadowColor = [UIColor darkGrayColor];
         _stackTitleLabel.shadowOffset = CGSizeMake(, );
         _stackTitleLabel.text = title;
         CGSize labelSize = [title sizeWithFont:_stackTitleLabel.font];
         rect.size.width = labelSize.width + ;
         _stackTitleLabel.frame = rect;
         [self addSubview:_stackTitleLabel];

         CGFloat width = labelSize.width + (_stackIimageView ? _stackIimageView.frame.size.width +  : );
         if(alignment == UITextAlignmentRight)
             frame.origin.x += (frame.size.width - width) - ;
         frame.size.width = width + ;
         self.frame = frame;

         rect = _stackIimageView.frame;
         rect.origin.x = alignment == UITextAlignmentLeft ?  : frame.size.width - frame.size.height;
         _stackIimageView.frame = rect;
     }
     return self;
 }

 - (id)initWithCoder:(NSCoder *)aDecoder
 {
     self = [super initWithCoder:aDecoder];
     if (self)
     {
         // Initialization code
     }
     return self;
 }

 - (id)initWithFrame:(CGRect)frame
 {
     self = [super initWithFrame:frame];
     if (self)
     {
         // Initialization code
     }
     return self;
 }

 CGAffineTransform CGAffineTransformMakeRotationAt(CGFloat angle, CGPoint pt)
 {
     const CGFloat fx = pt.x;
     const CGFloat fy = pt.y;
     const CGFloat fcos = cos(angle);
     const CGFloat fsin = sin(angle);
     return CGAffineTransformMake(fcos, fsin, -fsin, fcos, fx - fx * fcos + fy * fsin, fy - fx * fsin - fy * fcos);
 }

 @end

上面更多的是围绕布局而写了,没有过多的逻辑可言。

PCStackMenu

 #import <UIKit/UIKit.h>
 #import "PCStackMenuItem.h"

 typedef enum
 {
     PCStackMenuDirectionClockWiseUp = ,
     PCStackMenuDirectionClockWiseDown,
     PCStackMenuDirectionCounterClockWiseUp,
     PCStackMenuDirectionCounterClockWiseDown
 } PCStackMenuDirection;

 typedef void (^PCStackMenuBlock)(NSInteger selectedMenuIndex);

 @interface PCStackMenu : UIView
 {
     PCStackMenuBlock        _block;
 }

 @property (nonatomic, strong)    NSMutableArray        *items;

 + (PCStackMenu *)showStackMenuWithTitles:(NSArray *)titles
                               withImages:(NSArray *)images
                             atStartPoint:(CGPoint)startPoint
                                   inView:(UIView *)parent
                               itemHeight:(CGFloat)itemHeight
                            menuDirection:(PCStackMenuDirection)direction
                             onSelectMenu:(PCStackMenuBlock)block;

 - (PCStackMenu *)initWithTitles:(NSArray *)titles
                      withImages:(NSArray *)images
                    atStartPoint:(CGPoint)startPoint
                          inView:(UIView *)parent
                      itemHeight:(CGFloat)itemHeight
                   menuDirection:(PCStackMenuDirection)direction;

 - (void)show:(PCStackMenuBlock)block;
 - (void)dismiss;

 @end

而在PCStackMenu文件中,为弹出菜单枚举了四种弹出方向,下一行则是声明了一个返回值为空的块函数PCStackMenuBlock,这感觉很不错的编码格式,可以学习学习。

 #import "PCStackMenu.h"

 @interface PCStackMenu ()
 {
     UIWindow                *_overlayWindow;
     NSMutableArray            *_items;
     PCStackMenuDirection    _menuDirection;
 }

 @end

 @implementation PCStackMenu

 //算是设置Hightlight项吗?每一次状态的改变都对所有项进行重新设置
 - (void)clearHighlightWithExcept:(PCStackMenuItem *)selectedItem
 {
     for(PCStackMenuItem *item in _items)
         item.highlight = (item == selectedItem);
 }

 - (void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event
 {
     NSSet            *allTouches = [event allTouches];
     NSArray            *touchs = [allTouches allObjects];
     UITouch            *touch = [touchs lastObject];

     )
     {
         [self clearHighlightWithExcept:nil];
         PCStackMenuItem    *stackItem = nil;
         for(PCStackMenuItem *item in _items)
         {
             CGPoint point = [touch locationInView:item];
             if([item pointInside:point withEvent:event])
             {
                 stackItem = item;
                 [self clearHighlightWithExcept:item];
                 break;
             }
         }
         if(stackItem)
             stackItem.highlight = YES;
     }
 }

 - (void)touchesEnded:(NSSet *)touches withEvent:(UIEvent *)event
 {
     NSSet            *allTouches = [event allTouches];
     NSArray            *touchs = [allTouches allObjects];
     UITouch            *touch = [touchs lastObject];

     )
     {
         PCStackMenuItem    *stackItem = nil;
         for(PCStackMenuItem *item in _items)
         {
             CGPoint point = [touch locationInView:item];
             if([item pointInside:point withEvent:event])
             {
                 stackItem = item;
                 [self clearHighlightWithExcept:item];
                 break;
             }
         }
         if(stackItem)
         {
             [self clearHighlightWithExcept:stackItem];
             [self dismissWithSelect:stackItem];
         }
         else
         {
             [self clearHighlightWithExcept:nil];
             [self dismiss];
         }
     }
 }

 - (void)touchesMoved:(NSSet *)touches withEvent:(UIEvent *)event
 {
     NSSet                *allTouches = [event allTouches];
     NSArray                *touchs = [allTouches allObjects];
     UITouch                *touch = [touchs lastObject];

     )
     {
         PCStackMenuItem    *stackItem = nil;
         for(PCStackMenuItem *item in _items)
         {
             CGPoint point = [touch locationInView:item];
             if([item pointInside:point withEvent:event])
             {
                 stackItem = item;
                 [self clearHighlightWithExcept:item];
                 break;
             }
         }
         if(!stackItem)
             [self clearHighlightWithExcept:nil];
     }
 }

 - (void)touchesCancelled:(NSSet *)touches withEvent:(UIEvent *)event
 {
     [self clearHighlightWithExcept:nil];
 }

 - (void)show:(PCStackMenuBlock)block
 {
     _block = block;

     ; i < [_items count]; i++)
     {
         PCStackMenuItem *stackItem = [_items objectAtIndex:i];
         stackItem.hidden = NO;
     }

     [UIView animateWithDuration:0.2 animations:^{
         ; i < [_items count]; i++)
         {
             PCStackMenuItem *stackItem = [_items objectAtIndex:i];
             CGRect r = stackItem.frame;
             r.origin.y += i * r.size.height * (_menuDirection == PCStackMenuDirectionClockWiseUp || _menuDirection == PCStackMenuDirectionCounterClockWiseUp ? - : );
             stackItem.frame = r;
             CGPoint point = [self centerPointForRotation:stackItem];
             stackItem.transform = CGAffineTransformMakeRotationAt(i * (_menuDirection == PCStackMenuDirectionClockWiseUp || _menuDirection == PCStackMenuDirectionClockWiseDown ?  : -) * M_PI / , point);
             stackItem.alpha = 1.0;
         }
     } completion:^(BOOL finished) {
     }];
 }

 - (void)dismissWithSelect:(PCStackMenuItem *)selectedItem
 {
     int selectedIndex = [_items indexOfObject:selectedItem];
     selectedItem.highlight = YES;

     [UIView animateWithDuration:0.1 animations:^{
         CGRect r;
         ; i < [_items count]; i++)
         {
             PCStackMenuItem *stackItem = [_items objectAtIndex:i];
             )
                 r = stackItem.frame;
             if(selectedItem != stackItem)
             {
                 CGPoint point = [self centerPointForRotation:stackItem];
                 stackItem.transform = CGAffineTransformMakeRotationAt( * M_PI / , point);
                 stackItem.frame = r;
                 stackItem.alpha = 0.2;
             }
         }
     } completion:^(BOOL finished) {
         ; i < [_items count]; i++)
         {
             PCStackMenuItem *stackItem = [_items objectAtIndex:i];
             if(selectedItem != stackItem)
                 stackItem.hidden = YES;
         }
         [UIView animateWithDuration:0.2 animations:^{
             PCStackMenuItem *stackItem = [_items objectAtIndex:selectedIndex];
             CGRect r = ((UIView *)[_items objectAtIndex:]).frame;
             if(selectedItem == stackItem)
             {
                 CGPoint point = [self centerPointForRotation:stackItem];
                 stackItem.transform = CGAffineTransformMakeRotationAt( * M_PI / , point);
                 stackItem.frame = r;
                 stackItem.alpha = 0.5;
             }
         } completion:^(BOOL finished) {
             if(_block)
                 _block(selectedIndex);
             _overlayWindow = nil;
             _block = nil;
         }];
     }];
 }

 - (void)dismiss
 {
     [UIView animateWithDuration:0.2 animations:^{
         CGRect r;
         ; i < [_items count]; i++)
         {
             PCStackMenuItem *stackItem = [_items objectAtIndex:i];
             )
                 r = stackItem.frame;
             CGPoint point = [self centerPointForRotation:stackItem];
             stackItem.transform = CGAffineTransformMakeRotationAt( * M_PI / , point);
             stackItem.frame = r;
             stackItem.alpha = 0.2;
         }
     } completion:^(BOOL finished) {
         _block = nil;
         _overlayWindow = nil;
     }];
 }

 + (PCStackMenu *)showStackMenuWithTitles:(NSArray *)titles
                               withImages:(NSArray *)images
                             atStartPoint:(CGPoint)startPoint
                                   inView:(UIView *)parent
                               itemHeight:(CGFloat)itemHeight
                            menuDirection:(PCStackMenuDirection)direction
                             onSelectMenu:(PCStackMenuBlock)block
 {
     PCStackMenu *stackMenu = [[PCStackMenu alloc] initWithTitles:titles withImages:images atStartPoint:startPoint inView:parent itemHeight:itemHeight menuDirection:direction];
     [stackMenu show:block];
     return stackMenu;
 }

 - (PCStackMenu *)initWithTitles:(NSArray *)titles
                      withImages:(NSArray *)images
                    atStartPoint:(CGPoint)startPoint
                          inView:(UIView *)parent
                      itemHeight:(CGFloat)itemHeight
                   menuDirection:(PCStackMenuDirection)direction
 {
     self = [self initWithFrame:[UIScreen mainScreen].bounds];
     _menuDirection = direction;

     int max = MAX([titles count], [images count]);
     ; i < max; i++)
     {
         CGRect r = CGRectMake((direction == PCStackMenuDirectionClockWiseUp || direction == PCStackMenuDirectionCounterClockWiseDown) ?  : startPoint.x,
                               startPoint.y + (direction == PCStackMenuDirectionClockWiseDown || direction == PCStackMenuDirectionCounterClockWiseDown ?  : -itemHeight),
                               (direction == PCStackMenuDirectionClockWiseUp || direction == PCStackMenuDirectionCounterClockWiseDown) ? startPoint.x : self.frame.size.width - startPoint.x,
                               itemHeight);
         r = [self convertRect:r fromView:parent];
         NSString *title = (i < [titles count]) ? [titles objectAtIndex:i] : @"";
         UIImage *image = (i < [images count]) ? [images objectAtIndex:i] : nil;
         PCStackMenuItem *stackItem = [[PCStackMenuItem alloc] initWithFrame:r withTitle:title withImage:image alignment:(direction == PCStackMenuDirectionClockWiseUp || direction == PCStackMenuDirectionCounterClockWiseDown) ? UITextAlignmentRight : UITextAlignmentLeft];
         stackItem.alpha = 0.5;
         stackItem.hidden = YES;
         [self addSubview:stackItem];
         [_items addObject:stackItem];
     }

     return self;
 }

 - (CGPoint)centerPointForRotation:(PCStackMenuItem *)item
 {
     CGFloat x = _menuDirection == PCStackMenuDirectionClockWiseUp || _menuDirection == PCStackMenuDirectionCounterClockWiseDown ? self.frame.size.width : -self.frame.size.width;
     CGFloat y = _menuDirection == PCStackMenuDirectionClockWiseDown || _menuDirection == PCStackMenuDirectionCounterClockWiseDown ? -item.frame.origin.y : self.frame.size.height - item.frame.origin.y;
     return CGPointMake(x, y);
 }

 - (void)createOverlayWindow
 {
     _overlayWindow = [[UIWindow alloc] initWithFrame:[UIScreen mainScreen].bounds];
     _overlayWindow.autoresizingMask = UIViewAutoresizingFlexibleWidth | UIViewAutoresizingFlexibleHeight;
     _overlayWindow.backgroundColor = [UIColor clearColor];

     [_overlayWindow makeKeyAndVisible];
     [_overlayWindow addSubview:self];
 }

 - (id)initWithCoder:(NSCoder *)aDecoder
 {
     self = [super initWithCoder:aDecoder];
     if(self)
     {
         _items = [[NSMutableArray alloc] init];
         self.backgroundColor = [UIColor clearColor];
         [self createOverlayWindow];
     }
     return self;
 }

 - (id)initWithFrame:(CGRect)frame
 {
     self = [super initWithFrame:frame];
     if (self)
     {
         _items = [[NSMutableArray alloc] init];
         self.backgroundColor = [UIColor clearColor];
         [self createOverlayWindow];
     }
     return self;
 }

 @end

因为要休息了,更多的分析就等下一次进行修改再说吧。GoodNight

[转载-仅为个人学习所用]Stack Menu的更多相关文章

  1. 利用Mono.Cecil动态修改程序集来破解商业组件(仅用于研究学习)

    原文 利用Mono.Cecil动态修改程序集来破解商业组件(仅用于研究学习) Mono.Cecil是一个强大的MSIL的注入工具,利用它可以实现动态创建程序集,也可以实现拦截器横向切入动态方法,甚至还 ...

  2. 烂笔头@WP 的博文仅供自己学习的备忘录

    前记:本博主的博文仅供自己学习的备忘录. 说明:很久未用博客,登录密码已忘记,费劲找回来,特写本博文申明.因为,今天邮件收到一条博文的评论,有谩骂本博主之意,甚觉委屈.所以,写以下文字说明“1.我的博 ...

  3. 学习SpringBoot,整合全网各种优秀资源,SpringBoot基础,中间件,优质项目,博客资源等,仅供个人学习SpringBoot使用

    学习SpringBoot,整合全网各种优秀资源,SpringBoot基础,中间件,优质项目,博客资源等,仅供个人学习SpringBoot使用 一.SpringBoot系列教程 二.SpringBoot ...

  4. 去OpenCVManager,大部分为转载,仅当自己学习使用

    去OpenCVManager方法,可以参考这篇博客http://blog.csdn.net/yanzi1225627/article/details/27863615,可以用,挺好的.我这里只是做个总 ...

  5. 跳表的java实现,转载自网络,仅供自己学习使用

    文档结构: 1.代码结构 2.代码实现 1.代码结构 节点类: String key 键值 对跳跃表的操作都是根据键值进行的 Int value  实际值 Node  up,down,left,rig ...

  6. SQLyog试用到期的解决方法(仅供个人学习使用,禁止转载或用于商业盈利)

    作者:EzrealYi 本章链接:https://www.cnblogs.com/ezrealyi/p/12434105.html win+r->输入regedit->进入注册表 在计算机 ...

  7. 【转载】【JQuery学习】jQuery插件开发

    JQuery做得最好的就是他的闭包和扩展性.超级简单的扩展方法,让更多的人可以轻松的开发定制自己的jQuery插件.下面的东西是转载过来当做学习材料的.虽然貌似有点古老,但是jQuery的变更一直都不 ...

  8. 转载:reactor模式学习

    最近又重新看了下netty背后的设计思想,接触到了reactor模型.发现以前虽然也看过reactor和proactor模型的介绍,但是真的是只了解了个皮毛. 再重新学习了一遍,有了更深刻的认识.但是 ...

  9. Editor Scripting学习笔记之Menu Item

    主要用到: MenuItem属性 MenuCommand参数 可能用到: EditorApplication类 Selection类 GameObjectUtility类 FileUtil类 Undo ...

随机推荐

  1. load d3dcompiler_46.dll failed

    https://gist.github.com/rygorous/7936047 编shader的时候遇到这个warning不知道是不是什么隐患..从今天开始要做新项目了 尝试从同事那里要了这dll ...

  2. 常用的CSSreset整理

    说道CSSreset,大家又爱又恨,cssreset好处是,覆盖了浏览器的默认样式,使前端攻城狮能更加精确的添加样式,各个浏览器中的界面效果都相同.可是大量的.固定的CSSreset也给网页加载带来一 ...

  3. 转载淘宝UED响应十日谈

    响应式十日谈:楔子 响应式十日谈第一日:使用 rem 设置文字大小

  4. Ckeditor 中一些核心的对象的作用

    1.CKEditor CKEditor对象用于掌管全局,他是一个单例对象,管理着所有实例化了的编辑框. 通过replace方法创建编辑框实例. 2.CKEditor.editor 表示一个编辑框实例, ...

  5. 浅谈MySQL索引背后的数据结构及算法【转】

    摘要 本文以MySQL数据库为研究对象,讨论与数据库索引相关的一些话题.特别需要说明的是,MySQL支持诸多存储引擎,而各种存储引擎对索引的支持也各不相同,因此MySQL数据库支持多种索引类型,如BT ...

  6. 几种基于HTTP协议的RPC性能比较

    有了整体的了解后,可以发现Hessian的这个远程过程调用,完全使用动态代理来实现的,其实从客户端代码不难看出,HessianProxyFactory的create方法就是创建接口Basic的代理类, ...

  7. HDU 4148 Length of S(n)(字符串)

    题目 字符串处理 题意要猜,解析见代码: /* 这题每个S(n)是描述S(n-1)值 例如: S(1)=1; S(2)=11;即描述S(1)有1个1=11 S(3)=21;即描述S(2)有2个1=21 ...

  8. HDU 1698 Just a Hook (线段树区间更新)

    题目链接 题意 : 一个有n段长的金属棍,开始都涂上铜,分段涂成别的,金的值是3,银的值是2,铜的值是1,然后问你最后这n段总共的值是多少. 思路 : 线段树的区间更新.可以理解为线段树成段更新的模板 ...

  9. zoj 3057 Beans Game 博弈论

    思路:三维DP,刚开始用记忆化搜索,MLE…… 后来改为直接预处理所有的情况. 总之就是必败态的后继是必胜态!!! 代码如下: #include<iostream> #include< ...

  10. UVA 10341 二分搜索

    Solve the equation:p ∗ e−x + q ∗ sin(x) + r ∗ cos(x) + s ∗ tan(x) + t ∗ x2 + u = 0where 0 ≤ x ≤ 1.In ...