[转载-仅为个人学习所用]Stack Menu
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的更多相关文章
- 利用Mono.Cecil动态修改程序集来破解商业组件(仅用于研究学习)
原文 利用Mono.Cecil动态修改程序集来破解商业组件(仅用于研究学习) Mono.Cecil是一个强大的MSIL的注入工具,利用它可以实现动态创建程序集,也可以实现拦截器横向切入动态方法,甚至还 ...
- 烂笔头@WP 的博文仅供自己学习的备忘录
前记:本博主的博文仅供自己学习的备忘录. 说明:很久未用博客,登录密码已忘记,费劲找回来,特写本博文申明.因为,今天邮件收到一条博文的评论,有谩骂本博主之意,甚觉委屈.所以,写以下文字说明“1.我的博 ...
- 学习SpringBoot,整合全网各种优秀资源,SpringBoot基础,中间件,优质项目,博客资源等,仅供个人学习SpringBoot使用
学习SpringBoot,整合全网各种优秀资源,SpringBoot基础,中间件,优质项目,博客资源等,仅供个人学习SpringBoot使用 一.SpringBoot系列教程 二.SpringBoot ...
- 去OpenCVManager,大部分为转载,仅当自己学习使用
去OpenCVManager方法,可以参考这篇博客http://blog.csdn.net/yanzi1225627/article/details/27863615,可以用,挺好的.我这里只是做个总 ...
- 跳表的java实现,转载自网络,仅供自己学习使用
文档结构: 1.代码结构 2.代码实现 1.代码结构 节点类: String key 键值 对跳跃表的操作都是根据键值进行的 Int value 实际值 Node up,down,left,rig ...
- SQLyog试用到期的解决方法(仅供个人学习使用,禁止转载或用于商业盈利)
作者:EzrealYi 本章链接:https://www.cnblogs.com/ezrealyi/p/12434105.html win+r->输入regedit->进入注册表 在计算机 ...
- 【转载】【JQuery学习】jQuery插件开发
JQuery做得最好的就是他的闭包和扩展性.超级简单的扩展方法,让更多的人可以轻松的开发定制自己的jQuery插件.下面的东西是转载过来当做学习材料的.虽然貌似有点古老,但是jQuery的变更一直都不 ...
- 转载:reactor模式学习
最近又重新看了下netty背后的设计思想,接触到了reactor模型.发现以前虽然也看过reactor和proactor模型的介绍,但是真的是只了解了个皮毛. 再重新学习了一遍,有了更深刻的认识.但是 ...
- Editor Scripting学习笔记之Menu Item
主要用到: MenuItem属性 MenuCommand参数 可能用到: EditorApplication类 Selection类 GameObjectUtility类 FileUtil类 Undo ...
随机推荐
- c++ union
什么是union? 翻译过来说,就是共用体,或者也叫联合体.说到了union,也就是共用体,就不得不说一下struct了,当我们有如下的struct的定义时: 1 2 3 4 5 6 struct ...
- UML概述(转载)
UML是一种标准语言,用于指定,可视化,构造和文档的软件系统. UML是OMG在1997年1月提出了创建由对象管理组织(OMG)和UML1.0规范草案. OMG不断努力,使一个真正的行业标准. UML ...
- Python:异常处理
Python 是面向对象的语言,所以程序抛出的异常也是类. 一.常见的异常类 NameError:尝试访问一个没有申明的变量 ZeroDivisionError:除数为 0 SyntaxError:语 ...
- jQuery scroll事件
scroll事件适用于window对象,但也可滚动iframe框架与CSS overflow属性设置为scroll的元素. $(document).ready(function () { //本人习惯 ...
- 为jquery qrcode生成的二维码嵌入图片
在一次微信项目中,需要实现通过扫描二维码来进行会议签到,二维码的生成选择了qrcode.js的版本,然后使用jquery.qrcode.js插件来绘制二维码. <script type=&quo ...
- servlet 项目
1.Servlet基础类,必须继承HttpServlet package com.fan; import java.io.IOException; import java.io.PrintWriter ...
- 妙味课堂——HTML+CSS(第三课)
常见标签我已经在上一篇文章中提及,我们做前端设计时,主要也是用这些标签(最常用的). 然而有一个问题,就是有的标签都有自己的默认样式.试通过如下代码来说明: <!DOCTYPE html> ...
- lintcode:数字组合III
数字组合III 组给出两个整数n和k,返回从1......n中选出的k个数的组合. 您在真实的面试中是否遇到过这个题? Yes 样例 例如 n = 4 且 k = 2 返回的解为: [[2,4],[3 ...
- Spring学习总结(2)——Spring IOC的前世今生
前些天,参与了公司内部小组的一次技术交流,主要是针对<IOC与AOP>,本着学而时习之的态度及积极分享的精神,我就结合一个小故事来初浅地剖析一下我眼中的“IOC前世今生”,以方便初学者能更 ...
- 继电器Relay:ZZR08
继电器常识: 继电器有三个接线柱:常开(NO),常闭(NC),接地(C) 如果连接的时间长,偶尔需要断电, 那么接NC 和 C, 这样继电器set on 时为断电.除此之外,继电器还可以控制按键,以及 ...