[转载-仅为个人学习所用]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 ...
随机推荐
- 女性社区TOP10
“女性和孩子的钱是世界上最好赚的”并不是一句空话.据统计,女性掌管着家庭70%的支出,如果你能让女性为你掏出腰包,那么你基本就掌控了一个家庭的大部分的消费. 有趣的是,女性还是一个喜欢分享的群体,他们 ...
- Nginx使用webbench进行压力测试(转载)
在运维工作中,压力测试是一项非常重要的工作.比如在一个网站上线之前,能承受多大访问量.在大访问量情况下性能怎样,这些数据指标好坏将会直接影响用户体验. 但是,在压力测试中存在一个共性,那就是压力测试的 ...
- Web Component 文章
周末无意中了解了Web Component的概念. http://blog.amowu.com/2013/06/web-components.html http://www.v2ex.com/t/69 ...
- HDOJ 3177 Crixalis's Equipment
Crixalis's Equipment Time Limit: 2000/1000 MS (Java/Others) Memory Limit: 32768/32768 K (Java/Oth ...
- javascript设计模式--备忘录模式(Memento)
<!doctype html> <html lang="en"> <head> <meta charset="UTF-8&quo ...
- Sqli-labs less 38
Less-38 学习了关于stacked injection的相关知识,我们在本关可以得到直接的运用. 在执行select时的sql语句为:SELECT * FROM users WHERE id=' ...
- 常见的NoSql系统使用场景分析--转载
•Cassandra •特性:分布式与复制的权衡\根据列和键范围进行查询\BigTable类似的功能:列,列族\写比读快很多 •最佳适用:写操作较多,读比较少的时候.如果你的系统都是基于Java的时候 ...
- iOS-OpenGLES 简单渲染
UIImage *showImage = [UIImage imageNamed:, , showImage.size.width, showImage.size.height); ) fo ...
- [主席树]HDOJ2665 && POJ2104 && POJ2761
主席树真是神奇的物种! Orz一篇资料 题意:给n.m 下面有n个数 (编号1到n) 有m个询问,询问的是上面的数的编号在[l,r]之间第k小的数 n.m的范围都是$10^5$ 是主席树的入门题 ...
- linux c first
创建文件夹mkdir 文件夹名称删除文件夹rm -rf 文件夹名称创建文件touch test.c删除文件rm -f test.c编译gcc -o test test.c在执行只语句后会生成一个*te ...