123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888 |
- //
- // KxMenu.m
- // kxmenu project
- // https://github.com/kolyvan/kxmenu/
- //
- // Created by Kolyvan on 17.05.13.
- //
- /*
- Copyright (c) 2013 Konstantin Bukreev. All rights reserved.
-
- Redistribution and use in source and binary forms, with or without
- modification, are permitted provided that the following conditions are met:
-
- - Redistributions of source code must retain the above copyright notice, this
- list of conditions and the following disclaimer.
-
- - Redistributions in binary form must reproduce the above copyright notice,
- this list of conditions and the following disclaimer in the documentation
- and/or other materials provided with the distribution.
-
- THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
- AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
- IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
- DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
- FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
- DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
- SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
- CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
- OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
- OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
- */
- /*
- Some ideas was taken from QBPopupMenu project by Katsuma Tanaka.
- https://github.com/questbeat/QBPopupMenu
- */
- #import "KxMenu.h"
- #import <QuartzCore/QuartzCore.h>
- const CGFloat kArrowSize = 12.f;
- ////////////////////////////////////////////////////////////////////////////////
- ////////////////////////////////////////////////////////////////////////////////
- @interface KxMenuView : UIView
- @end
- @interface KxMenuOverlay : UIView
- @end
- @implementation KxMenuOverlay
- // - (void) dealloc { NSLog(@"dealloc %@", self); }
- - (id)initWithFrame:(CGRect)frame
- {
- self = [super initWithFrame:frame];
- if (self) {
- self.backgroundColor = [UIColor clearColor];
- self.opaque = NO;
-
- UITapGestureRecognizer *gestureRecognizer;
- gestureRecognizer = [[UITapGestureRecognizer alloc] initWithTarget:self
- action:@selector(singleTap:)];
- [self addGestureRecognizer:gestureRecognizer];
- }
- return self;
- }
- // thank horaceho https://github.com/horaceho
- // for his solution described in https://github.com/kolyvan/kxmenu/issues/9
- - (void)singleTap:(UITapGestureRecognizer *)recognizer
- {
- for (UIView *v in self.subviews) {
- if ([v isKindOfClass:[KxMenuView class]] && [v respondsToSelector:@selector(dismissMenu:)]) {
- [v performSelector:@selector(dismissMenu:) withObject:@(YES)];
- }
- }
- }
- @end
- ////////////////////////////////////////////////////////////////////////////////
- ////////////////////////////////////////////////////////////////////////////////
- @implementation KxMenuItem
- + (instancetype) menuItem:(NSString *) title
- image:(UIImage *) image
- target:(id)target
- action:(SEL) action
- {
- return [[KxMenuItem alloc] init:title
- image:image
- target:target
- action:action];
- }
- - (id) init:(NSString *) title
- image:(UIImage *) image
- target:(id)target
- action:(SEL) action
- {
- NSParameterAssert(title.length || image);
-
- self = [super init];
- if (self) {
-
- _title = title;
- _image = image;
- _target = target;
- _action = action;
- }
- return self;
- }
- - (BOOL) enabled
- {
- return _target != nil && _action != NULL;
- }
- - (void) performAction
- {
- __strong id target = self.target;
-
- if (target && [target respondsToSelector:_action]) {
-
- [target performSelectorOnMainThread:_action withObject:self waitUntilDone:YES];
- }
- }
- - (NSString *) description
- {
- return [NSString stringWithFormat:@"<%@ #%p %@>", [self class], self, _title];
- }
- @end
- ////////////////////////////////////////////////////////////////////////////////
- ////////////////////////////////////////////////////////////////////////////////
- typedef enum {
-
- KxMenuViewArrowDirectionNone,
- KxMenuViewArrowDirectionUp,
- KxMenuViewArrowDirectionDown,
- KxMenuViewArrowDirectionLeft,
- KxMenuViewArrowDirectionRight,
-
- } KxMenuViewArrowDirection;
- @implementation KxMenuView {
-
- KxMenuViewArrowDirection _arrowDirection;
- CGFloat _arrowPosition;
- UIView *_contentView;
- NSArray *_menuItems;
- }
- - (id)init
- {
- self = [super initWithFrame:CGRectZero];
- if(self) {
- self.backgroundColor = [UIColor clearColor];
- self.opaque = YES;
- self.alpha = 0;
-
- self.layer.shadowOpacity = 0.5;
- self.layer.shadowOffset = CGSizeMake(2, 2);
- self.layer.shadowRadius = 2;
- }
-
- return self;
- }
- // - (void) dealloc { NSLog(@"dealloc %@", self); }
- - (void) setupFrameInView:(UIView *)view
- fromRect:(CGRect)fromRect
- {
- const CGSize contentSize = _contentView.frame.size;
-
- const CGFloat outerWidth = view.bounds.size.width;
- const CGFloat outerHeight = view.bounds.size.height;
-
- const CGFloat rectX0 = fromRect.origin.x;
- const CGFloat rectX1 = fromRect.origin.x + fromRect.size.width;
- const CGFloat rectXM = fromRect.origin.x + fromRect.size.width * 0.5f;
- const CGFloat rectY0 = fromRect.origin.y;
- const CGFloat rectY1 = fromRect.origin.y + fromRect.size.height;
- const CGFloat rectYM = fromRect.origin.y + fromRect.size.height * 0.5f;;
-
- const CGFloat widthPlusArrow = contentSize.width + kArrowSize;
- const CGFloat heightPlusArrow = contentSize.height + kArrowSize;
- const CGFloat widthHalf = contentSize.width * 0.5f;
- const CGFloat heightHalf = contentSize.height * 0.5f;
-
- const CGFloat kMargin = 5.f;
-
- if (heightPlusArrow < (outerHeight - rectY1)) {
-
- _arrowDirection = KxMenuViewArrowDirectionUp;
- CGPoint point = (CGPoint){
- rectXM - widthHalf,
- rectY1
- };
-
- if (point.x < kMargin)
- point.x = kMargin;
-
- if ((point.x + contentSize.width + kMargin) > outerWidth)
- point.x = outerWidth - contentSize.width - kMargin;
-
- _arrowPosition = rectXM - point.x;
- //_arrowPosition = MAX(16, MIN(_arrowPosition, contentSize.width - 16));
- _contentView.frame = (CGRect){0, kArrowSize, contentSize};
-
- self.frame = (CGRect) {
-
- point,
- contentSize.width,
- contentSize.height + kArrowSize
- };
-
- } else if (heightPlusArrow < rectY0) {
-
- _arrowDirection = KxMenuViewArrowDirectionDown;
- CGPoint point = (CGPoint){
- rectXM - widthHalf,
- rectY0 - heightPlusArrow
- };
-
- if (point.x < kMargin)
- point.x = kMargin;
-
- if ((point.x + contentSize.width + kMargin) > outerWidth)
- point.x = outerWidth - contentSize.width - kMargin;
-
- _arrowPosition = rectXM - point.x;
- _contentView.frame = (CGRect){CGPointZero, contentSize};
-
- self.frame = (CGRect) {
-
- point,
- contentSize.width,
- contentSize.height + kArrowSize
- };
-
- } else if (widthPlusArrow < (outerWidth - rectX1)) {
-
- _arrowDirection = KxMenuViewArrowDirectionLeft;
- CGPoint point = (CGPoint){
- rectX1,
- rectYM - heightHalf
- };
-
- if (point.y < kMargin)
- point.y = kMargin;
-
- if ((point.y + contentSize.height + kMargin) > outerHeight)
- point.y = outerHeight - contentSize.height - kMargin;
-
- _arrowPosition = rectYM - point.y;
- _contentView.frame = (CGRect){kArrowSize, 0, contentSize};
-
- self.frame = (CGRect) {
-
- point,
- contentSize.width + kArrowSize,
- contentSize.height
- };
-
- } else if (widthPlusArrow < rectX0) {
-
- _arrowDirection = KxMenuViewArrowDirectionRight;
- CGPoint point = (CGPoint){
- rectX0 - widthPlusArrow,
- rectYM - heightHalf
- };
-
- if (point.y < kMargin)
- point.y = kMargin;
-
- if ((point.y + contentSize.height + 5) > outerHeight)
- point.y = outerHeight - contentSize.height - kMargin;
-
- _arrowPosition = rectYM - point.y;
- _contentView.frame = (CGRect){CGPointZero, contentSize};
-
- self.frame = (CGRect) {
-
- point,
- contentSize.width + kArrowSize,
- contentSize.height
- };
-
- } else {
-
- _arrowDirection = KxMenuViewArrowDirectionNone;
-
- self.frame = (CGRect) {
-
- (outerWidth - contentSize.width) * 0.5f,
- (outerHeight - contentSize.height) * 0.5f,
- contentSize,
- };
- }
- }
- - (void)showMenuInView:(UIView *)view
- fromRect:(CGRect)rect
- menuItems:(NSArray *)menuItems
- {
- _menuItems = menuItems;
-
- _contentView = [self mkContentView];
- [self addSubview:_contentView];
-
- [self setupFrameInView:view fromRect:rect];
-
- KxMenuOverlay *overlay = [[KxMenuOverlay alloc] initWithFrame:view.bounds];
- [overlay addSubview:self];
- [view addSubview:overlay];
-
- _contentView.hidden = YES;
- const CGRect toFrame = self.frame;
- self.frame = (CGRect){self.arrowPoint, 1, 1};
-
- [UIView animateWithDuration:0.2
- animations:^(void) {
-
- self.alpha = 1.0f;
- self.frame = toFrame;
-
- } completion:^(BOOL completed) {
- _contentView.hidden = NO;
- }];
-
- }
- - (void)dismissMenu:(BOOL) animated
- {
- if (self.superview) {
-
- if (animated) {
-
- _contentView.hidden = YES;
- const CGRect toFrame = (CGRect){self.arrowPoint, 1, 1};
-
- [UIView animateWithDuration:0.2
- animations:^(void) {
-
- self.alpha = 0;
- self.frame = toFrame;
-
- } completion:^(BOOL finished) {
-
- if ([self.superview isKindOfClass:[KxMenuOverlay class]])
- [self.superview removeFromSuperview];
- [self removeFromSuperview];
- }];
-
- } else {
-
- if ([self.superview isKindOfClass:[KxMenuOverlay class]])
- [self.superview removeFromSuperview];
- [self removeFromSuperview];
- }
- }
- }
- - (void)performAction:(id)sender
- {
- [self dismissMenu:YES];
-
- UIButton *button = (UIButton *)sender;
- KxMenuItem *menuItem = _menuItems[button.tag];
- [menuItem performAction];
- }
- - (UIView *) mkContentView
- {
- for (UIView *v in self.subviews) {
- [v removeFromSuperview];
- }
-
- if (!_menuItems.count)
- return nil;
-
- const CGFloat kMinMenuItemHeight = 32.f;
- const CGFloat kMinMenuItemWidth = 32.f;
- const CGFloat kMarginX = 10.f;
- const CGFloat kMarginY = 5.f;
-
- UIFont *titleFont = [KxMenu titleFont];
- if (!titleFont) titleFont = [UIFont boldSystemFontOfSize:16];
-
- CGFloat maxImageWidth = 0;
- CGFloat maxItemHeight = 0;
- CGFloat maxItemWidth = 0;
-
- for (KxMenuItem *menuItem in _menuItems) {
-
- const CGSize imageSize = menuItem.image.size;
- if (imageSize.width > maxImageWidth)
- maxImageWidth = imageSize.width;
- }
-
- if (maxImageWidth) {
- maxImageWidth += kMarginX;
- }
-
- for (KxMenuItem *menuItem in _menuItems) {
- const CGSize titleSize = [menuItem.title sizeWithFont:titleFont];
- const CGSize imageSize = menuItem.image.size;
- const CGFloat itemHeight = MAX(titleSize.height, imageSize.height) + kMarginY * 2;
- const CGFloat itemWidth = ((!menuItem.enabled && !menuItem.image) ? titleSize.width : maxImageWidth + titleSize.width) + kMarginX * 4;
-
- if (itemHeight > maxItemHeight)
- maxItemHeight = itemHeight;
-
- if (itemWidth > maxItemWidth)
- maxItemWidth = itemWidth;
- }
-
- maxItemWidth = MAX(maxItemWidth, kMinMenuItemWidth);
- maxItemHeight = MAX(maxItemHeight, kMinMenuItemHeight);
- const CGFloat titleX = kMarginX * 2 + maxImageWidth;
- const CGFloat titleWidth = maxItemWidth - titleX - kMarginX * 2;
-
- UIImage *selectedImage = [KxMenuView selectedImage:(CGSize){maxItemWidth, maxItemHeight + 2}];
- UIImage *gradientLine = [KxMenuView gradientLine: (CGSize){maxItemWidth - kMarginX * 4, 1}];
-
- UIView *contentView = [[UIView alloc] initWithFrame:CGRectZero];
- contentView.autoresizingMask = UIViewAutoresizingNone;
- contentView.backgroundColor = [UIColor clearColor];
- contentView.opaque = NO;
-
- CGFloat itemY = kMarginY * 2;
- NSUInteger itemNum = 0;
-
- for (KxMenuItem *menuItem in _menuItems) {
-
- const CGRect itemFrame = (CGRect){0, itemY, maxItemWidth, maxItemHeight};
-
- UIView *itemView = [[UIView alloc] initWithFrame:itemFrame];
- itemView.autoresizingMask = UIViewAutoresizingNone;
- itemView.backgroundColor = [UIColor clearColor];
- itemView.opaque = NO;
-
- [contentView addSubview:itemView];
-
- if (menuItem.enabled) {
-
- UIButton *button = [UIButton buttonWithType:UIButtonTypeCustom];
- button.tag = itemNum;
- button.frame = itemView.bounds;
- button.enabled = menuItem.enabled;
- button.backgroundColor = [UIColor clearColor];
- button.opaque = NO;
- button.autoresizingMask = UIViewAutoresizingNone;
-
- [button addTarget:self
- action:@selector(performAction:)
- forControlEvents:UIControlEventTouchUpInside];
-
- [button setBackgroundImage:selectedImage forState:UIControlStateHighlighted];
-
- [itemView addSubview:button];
- }
-
- if (menuItem.title.length) {
-
- CGRect titleFrame;
-
- if (!menuItem.enabled && !menuItem.image) {
-
- titleFrame = (CGRect){
- kMarginX * 2,
- kMarginY,
- maxItemWidth - kMarginX * 4,
- maxItemHeight - kMarginY * 2
- };
-
- } else {
-
- titleFrame = (CGRect){
- titleX,
- kMarginY,
- titleWidth,
- maxItemHeight - kMarginY * 2
- };
- }
-
- UILabel *titleLabel = [[UILabel alloc] initWithFrame:titleFrame];
- titleLabel.text = menuItem.title;
- titleLabel.font = titleFont;
- titleLabel.textAlignment = menuItem.alignment;
- titleLabel.textColor = menuItem.foreColor ? menuItem.foreColor : [UIColor whiteColor];
- titleLabel.backgroundColor = [UIColor clearColor];
- titleLabel.autoresizingMask = UIViewAutoresizingNone;
- //titleLabel.backgroundColor = [UIColor greenColor];
- [itemView addSubview:titleLabel];
- }
-
- if (menuItem.image) {
-
- const CGRect imageFrame = {kMarginX * 2, kMarginY, maxImageWidth, maxItemHeight - kMarginY * 2};
- UIImageView *imageView = [[UIImageView alloc] initWithFrame:imageFrame];
- imageView.image = menuItem.image;
- imageView.clipsToBounds = YES;
- imageView.contentMode = UIViewContentModeCenter;
- imageView.autoresizingMask = UIViewAutoresizingNone;
- [itemView addSubview:imageView];
- }
-
- if (itemNum < _menuItems.count - 1) {
-
- UIImageView *gradientView = [[UIImageView alloc] initWithImage:gradientLine];
- gradientView.frame = (CGRect){kMarginX * 2, maxItemHeight + 1, gradientLine.size};
- gradientView.contentMode = UIViewContentModeLeft;
- [itemView addSubview:gradientView];
-
- itemY += 2;
- }
-
- itemY += maxItemHeight;
- ++itemNum;
- }
-
- contentView.frame = (CGRect){0, 0, maxItemWidth, itemY + kMarginY * 2};
-
- return contentView;
- }
- - (CGPoint) arrowPoint
- {
- CGPoint point;
-
- if (_arrowDirection == KxMenuViewArrowDirectionUp) {
-
- point = (CGPoint){ CGRectGetMinX(self.frame) + _arrowPosition, CGRectGetMinY(self.frame) };
-
- } else if (_arrowDirection == KxMenuViewArrowDirectionDown) {
-
- point = (CGPoint){ CGRectGetMinX(self.frame) + _arrowPosition, CGRectGetMaxY(self.frame) };
-
- } else if (_arrowDirection == KxMenuViewArrowDirectionLeft) {
-
- point = (CGPoint){ CGRectGetMinX(self.frame), CGRectGetMinY(self.frame) + _arrowPosition };
-
- } else if (_arrowDirection == KxMenuViewArrowDirectionRight) {
-
- point = (CGPoint){ CGRectGetMaxX(self.frame), CGRectGetMinY(self.frame) + _arrowPosition };
-
- } else {
-
- point = self.center;
- }
-
- return point;
- }
- + (UIImage *) selectedImage: (CGSize) size
- {
- const CGFloat locations[] = {0,1};
- const CGFloat components[] = {
- 0.216, 0.471, 0.871, 1,
- 0.059, 0.353, 0.839, 1,
- };
-
- return [self gradientImageWithSize:size locations:locations components:components count:2];
- }
- + (UIImage *) gradientLine: (CGSize) size
- {
- const CGFloat locations[5] = {0,0.2,0.5,0.8,1};
-
- const CGFloat R = 0.44f, G = 0.44f, B = 0.44f;
-
- const CGFloat components[20] = {
- R,G,B,0.1,
- R,G,B,0.4,
- R,G,B,0.7,
- R,G,B,0.4,
- R,G,B,0.1
- };
-
- return [self gradientImageWithSize:size locations:locations components:components count:5];
- }
- + (UIImage *) gradientImageWithSize:(CGSize) size
- locations:(const CGFloat []) locations
- components:(const CGFloat []) components
- count:(NSUInteger)count
- {
- UIGraphicsBeginImageContextWithOptions(size, NO, 0);
- CGContextRef context = UIGraphicsGetCurrentContext();
-
- CGColorSpaceRef colorSpace = CGColorSpaceCreateDeviceRGB();
- CGGradientRef colorGradient = CGGradientCreateWithColorComponents(colorSpace, components, locations, 2);
- CGColorSpaceRelease(colorSpace);
- CGContextDrawLinearGradient(context, colorGradient, (CGPoint){0, 0}, (CGPoint){size.width, 0}, 0);
- CGGradientRelease(colorGradient);
-
- UIImage *image = UIGraphicsGetImageFromCurrentImageContext();
- UIGraphicsEndImageContext();
- return image;
- }
- - (void) drawRect:(CGRect)rect
- {
- [self drawBackground:self.bounds
- inContext:UIGraphicsGetCurrentContext()];
- }
- - (void)drawBackground:(CGRect)frame
- inContext:(CGContextRef) context
- {
- CGFloat R0 = 0.267, G0 = 0.303, B0 = 0.335;
- CGFloat R1 = 0.040, G1 = 0.040, B1 = 0.040;
-
- UIColor *tintColor = [KxMenu tintColor];
- if (tintColor) {
-
- CGFloat a;
- [tintColor getRed:&R0 green:&G0 blue:&B0 alpha:&a];
- }
-
- CGFloat X0 = frame.origin.x;
- CGFloat X1 = frame.origin.x + frame.size.width;
- CGFloat Y0 = frame.origin.y;
- CGFloat Y1 = frame.origin.y + frame.size.height;
-
- // render arrow
-
- UIBezierPath *arrowPath = [UIBezierPath bezierPath];
-
- // fix the issue with gap of arrow's base if on the edge
- const CGFloat kEmbedFix = 3.f;
-
- if (_arrowDirection == KxMenuViewArrowDirectionUp) {
-
- const CGFloat arrowXM = _arrowPosition;
- const CGFloat arrowX0 = arrowXM - kArrowSize;
- const CGFloat arrowX1 = arrowXM + kArrowSize;
- const CGFloat arrowY0 = Y0;
- const CGFloat arrowY1 = Y0 + kArrowSize + kEmbedFix;
-
- [arrowPath moveToPoint: (CGPoint){arrowXM, arrowY0}];
- [arrowPath addLineToPoint: (CGPoint){arrowX1, arrowY1}];
- [arrowPath addLineToPoint: (CGPoint){arrowX0, arrowY1}];
- [arrowPath addLineToPoint: (CGPoint){arrowXM, arrowY0}];
-
- [[UIColor colorWithRed:R0 green:G0 blue:B0 alpha:1] set];
-
- Y0 += kArrowSize;
-
- } else if (_arrowDirection == KxMenuViewArrowDirectionDown) {
-
- const CGFloat arrowXM = _arrowPosition;
- const CGFloat arrowX0 = arrowXM - kArrowSize;
- const CGFloat arrowX1 = arrowXM + kArrowSize;
- const CGFloat arrowY0 = Y1 - kArrowSize - kEmbedFix;
- const CGFloat arrowY1 = Y1;
-
- [arrowPath moveToPoint: (CGPoint){arrowXM, arrowY1}];
- [arrowPath addLineToPoint: (CGPoint){arrowX1, arrowY0}];
- [arrowPath addLineToPoint: (CGPoint){arrowX0, arrowY0}];
- [arrowPath addLineToPoint: (CGPoint){arrowXM, arrowY1}];
-
- [[UIColor colorWithRed:R1 green:G1 blue:B1 alpha:1] set];
-
- Y1 -= kArrowSize;
-
- } else if (_arrowDirection == KxMenuViewArrowDirectionLeft) {
-
- const CGFloat arrowYM = _arrowPosition;
- const CGFloat arrowX0 = X0;
- const CGFloat arrowX1 = X0 + kArrowSize + kEmbedFix;
- const CGFloat arrowY0 = arrowYM - kArrowSize;;
- const CGFloat arrowY1 = arrowYM + kArrowSize;
-
- [arrowPath moveToPoint: (CGPoint){arrowX0, arrowYM}];
- [arrowPath addLineToPoint: (CGPoint){arrowX1, arrowY0}];
- [arrowPath addLineToPoint: (CGPoint){arrowX1, arrowY1}];
- [arrowPath addLineToPoint: (CGPoint){arrowX0, arrowYM}];
-
- [[UIColor colorWithRed:R0 green:G0 blue:B0 alpha:1] set];
-
- X0 += kArrowSize;
-
- } else if (_arrowDirection == KxMenuViewArrowDirectionRight) {
-
- const CGFloat arrowYM = _arrowPosition;
- const CGFloat arrowX0 = X1;
- const CGFloat arrowX1 = X1 - kArrowSize - kEmbedFix;
- const CGFloat arrowY0 = arrowYM - kArrowSize;;
- const CGFloat arrowY1 = arrowYM + kArrowSize;
-
- [arrowPath moveToPoint: (CGPoint){arrowX0, arrowYM}];
- [arrowPath addLineToPoint: (CGPoint){arrowX1, arrowY0}];
- [arrowPath addLineToPoint: (CGPoint){arrowX1, arrowY1}];
- [arrowPath addLineToPoint: (CGPoint){arrowX0, arrowYM}];
-
- [[UIColor colorWithRed:R1 green:G1 blue:B1 alpha:1] set];
-
- X1 -= kArrowSize;
- }
-
- [arrowPath fill];
- // render body
-
- const CGRect bodyFrame = {X0, Y0, X1 - X0, Y1 - Y0};
-
- UIBezierPath *borderPath = [UIBezierPath bezierPathWithRoundedRect:bodyFrame
- cornerRadius:8];
-
- const CGFloat locations[] = {0, 1};
- const CGFloat components[] = {
- R0, G0, B0, 1,
- R1, G1, B1, 1,
- };
-
- CGColorSpaceRef colorSpace = CGColorSpaceCreateDeviceRGB();
- CGGradientRef gradient = CGGradientCreateWithColorComponents(colorSpace,
- components,
- locations,
- sizeof(locations)/sizeof(locations[0]));
- CGColorSpaceRelease(colorSpace);
-
-
- [borderPath addClip];
-
- CGPoint start, end;
-
- if (_arrowDirection == KxMenuViewArrowDirectionLeft ||
- _arrowDirection == KxMenuViewArrowDirectionRight) {
-
- start = (CGPoint){X0, Y0};
- end = (CGPoint){X1, Y0};
-
- } else {
-
- start = (CGPoint){X0, Y0};
- end = (CGPoint){X0, Y1};
- }
-
- CGContextDrawLinearGradient(context, gradient, start, end, 0);
-
- CGGradientRelease(gradient);
- }
- @end
- ////////////////////////////////////////////////////////////////////////////////
- ////////////////////////////////////////////////////////////////////////////////
- static KxMenu *gMenu;
- static UIColor *gTintColor;
- static UIFont *gTitleFont;
- @implementation KxMenu {
-
- KxMenuView *_menuView;
- BOOL _observing;
- }
- + (instancetype) sharedMenu
- {
- static dispatch_once_t onceToken;
- dispatch_once(&onceToken, ^{
-
- gMenu = [[KxMenu alloc] init];
- });
- return gMenu;
- }
- - (id) init
- {
- NSAssert(!gMenu, @"singleton object");
-
- self = [super init];
- if (self) {
- }
- return self;
- }
- - (void) dealloc
- {
- if (_observing) {
- [[NSNotificationCenter defaultCenter] removeObserver:self];
- }
- }
- - (void) showMenuInView:(UIView *)view
- fromRect:(CGRect)rect
- menuItems:(NSArray *)menuItems
- {
- NSParameterAssert(view);
- NSParameterAssert(menuItems.count);
-
- if (_menuView) {
-
- [_menuView dismissMenu:NO];
- _menuView = nil;
- }
- if (!_observing) {
-
- _observing = YES;
-
- [[NSNotificationCenter defaultCenter] addObserver:self
- selector:@selector(orientationWillChange:)
- name:UIApplicationWillChangeStatusBarOrientationNotification
- object:nil];
- }
-
- _menuView = [[KxMenuView alloc] init];
- [_menuView showMenuInView:view fromRect:rect menuItems:menuItems];
- }
- - (void) dismissMenu
- {
- if (_menuView) {
-
- [_menuView dismissMenu:NO];
- _menuView = nil;
- }
-
- if (_observing) {
-
- _observing = NO;
- [[NSNotificationCenter defaultCenter] removeObserver:self];
- }
- }
- - (void) orientationWillChange: (NSNotification *) n
- {
- [self dismissMenu];
- }
- + (void) showMenuInView:(UIView *)view
- fromRect:(CGRect)rect
- menuItems:(NSArray *)menuItems
- {
- [[self sharedMenu] showMenuInView:view fromRect:rect menuItems:menuItems];
- }
- + (void) dismissMenu
- {
- [[self sharedMenu] dismissMenu];
- }
- + (UIColor *) tintColor
- {
- return gTintColor;
- }
- + (void) setTintColor: (UIColor *) tintColor
- {
- if (tintColor != gTintColor) {
- gTintColor = tintColor;
- }
- }
- + (UIFont *) titleFont
- {
- return gTitleFont;
- }
- + (void) setTitleFont: (UIFont *) titleFont
- {
- if (titleFont != gTitleFont) {
- gTitleFont = titleFont;
- }
- }
- @end
|