[翻译] VLDContextSheet

VLDContextSheet

[翻译] VLDContextSheet

效果:

[翻译] VLDContextSheet

A clone of the Pinterest iOS app context menu.

复制了Pinterest应用的菜单效果。

 

Example Usage - 使用样例

VLDContextSheetItem *item1 = [[VLDContextSheetItem alloc] initWithTitle: @"Gift" image: [UIImage imageNamed: @"gift"] highlightedImage: [UIImage imageNamed: @"gift_highlighted"]]; VLDContextSheetItem *item2 = ... VLDContextSheetItem *item3 = ... self.contextSheet = [[VLDContextSheet alloc] initWithItems: @[ item1, item2, item3 ]]; self.contextSheet.delegate = self;

Show - 显示

- (void) longPressed: (UIGestureRecognizer *) gestureRecognizer {

    if(gestureRecognizer.state == UIGestureRecognizerStateBegan) { [self.contextSheet startWithGestureRecognizer: gestureRecognizer inView: self.view]; } }

Delegate method - 代理方法

- (void) contextSheet: (VLDContextSheet *) contextSheet didSelectItem: (VLDContextSheetItem *) item {

    NSLog(@"Selected item: %@", item.title); }

Hide - 隐藏

[self.contextSheet end];

For more info check the Example project.

你可以在示例代码里面查看更多的信息。

 

附录源码:

VLDContextSheetItem.h 与 VLDContextSheetItem.m

//

//  VLDContextSheetItem.h

//

//  Created by Vladimir Angelov on 2/10/14.

//  Copyright (c) 2014 Vladimir Angelov. All rights reserved.

//



#import <Foundation/Foundation.h>



@interface VLDContextSheetItem : NSObject



- (id) initWithTitle: (NSString *) title

               image: (UIImage *) image

    highlightedImage: (UIImage *) highlightedImage;



@property (strong, readonly) NSString *title;

@property (strong, readonly) UIImage *image;

@property (strong, readonly) UIImage *highlightedImage;



@property (assign, readwrite, getter = isEnabled) BOOL enabled;



@end
//

//  VLDContextSheetItem.m

//

//  Created by Vladimir Angelov on 2/10/14.

//  Copyright (c) 2014 Vladimir Angelov. All rights reserved.

//



#import "VLDContextSheetItem.h"



@implementation VLDContextSheetItem



- (id) initWithTitle: (NSString *) title

               image: (UIImage *) image

    highlightedImage: (UIImage *) highlightedImage {

    

    self = [super init];

    

    if(self) {

        _title = title;

        _image = image;

        _highlightedImage = highlightedImage;

        _enabled = YES;

    }

    

    return self;

}



@end

VLDContextSheetItem.h 与 VLDContextSheetItem.m

//

//  VLDContextSheetItem.h

//

//  Created by Vladimir Angelov on 2/9/14.

//  Copyright (c) 2014 Vladimir Angelov. All rights reserved.

//



#import <Foundation/Foundation.h>



@class VLDContextSheetItem;



@interface VLDContextSheetItemView : UIView



@property (strong, nonatomic) VLDContextSheetItem *item;

@property (readonly) BOOL isHighlighted;



- (void) setHighlighted: (BOOL) highlighted animated: (BOOL) animated;



@end
//

//  VLDContextSheetItemView.m,

//

//  Created by Vladimir Angelov on 2/9/14.

//  Copyright (c) 2014 Vladimir Angelov. All rights reserved.

//



#import "VLDContextSheetItemView.h"

#import "VLDContextSheetItem.h"



#import <CoreImage/CoreImage.h>





static const NSInteger VLDTextPadding = 5;



@interface VLDContextSheetItemView ()



@property (nonatomic, strong) UIImageView *imageView;

@property (nonatomic, strong) UIImageView *highlightedImageView;

@property (nonatomic, strong) UILabel *label;

@property (nonatomic, assign) NSInteger labelWidth;



@end



@implementation VLDContextSheetItemView



@synthesize item = _item;



- (id) initWithFrame: (CGRect) frame {

    self = [super initWithFrame: CGRectMake(0, 0, 50, 83)];

    

    if(self) {

        [self createSubviews];

    }

    

    return self;

}



- (void) createSubviews {

    _imageView = [[UIImageView alloc] init];

    [self addSubview: _imageView];

    

    _highlightedImageView = [[UIImageView alloc] init];

    _highlightedImageView.alpha = 0.0;

    [self addSubview: _highlightedImageView];

    

    _label = [[UILabel alloc] init];

    _label.clipsToBounds = YES;

    _label.font = [UIFont systemFontOfSize: 10];

    _label.textAlignment = NSTextAlignmentCenter;

    _label.layer.cornerRadius = 7;

    _label.backgroundColor = [UIColor colorWithWhite: 0.0 alpha: 0.4];

    _label.textColor = [UIColor whiteColor];

    _label.alpha = 0.0;

    [self addSubview: _label];

}



- (void) layoutSubviews {

    [super layoutSubviews];

    

    self.imageView.frame = CGRectMake(0, (self.frame.size.height - self.frame.size.width) / 2, self.frame.size.width, self.frame.size.width);

    self.highlightedImageView.frame = self.imageView.frame;

    self.label.frame = CGRectMake((self.frame.size.width - self.labelWidth) / 2.0, 0, self.labelWidth, 14);

}



- (void) setItem:(VLDContextSheetItem *)item {

    _item = item;

    

    [self updateImages];

    [self updateLabelText];

}



- (void) updateImages {

    self.imageView.image = self.item.image;

    self.highlightedImageView.image = self.item.highlightedImage;

    

    self.imageView.alpha = self.item.isEnabled ? 1.0 : 0.3;

}



- (void) updateLabelText {

    self.label.text = self.item.title;

    self.labelWidth = 2 * VLDTextPadding + ceil([self.label.text sizeWithAttributes: @{ NSFontAttributeName: self.label.font }].width);

    [self setNeedsDisplay];

}



- (void) setHighlighted: (BOOL) highlighted animated: (BOOL) animated {

    if (!self.item.isEnabled) {

        return;

    }



    _isHighlighted = highlighted;

    

    [UIView animateWithDuration: animated ? 0.3 : 0.0

                          delay: 0.0

                        options: UIViewAnimationOptionCurveEaseInOut

                     animations:^{

                         self.highlightedImageView.alpha = (highlighted ? 1.0 : 0.0);

                         self.imageView.alpha = 1 - self.highlightedImageView.alpha;

                         self.label.alpha = self.highlightedImageView.alpha;

                         

                     }

                     completion: nil];

    

    

    

}



@end

VLDContextSheet.h 与 VLDContextSheet.m

//

//  VLDContextSheet.h

//

//  Created by Vladimir Angelov on 2/7/14.

//  Copyright (c) 2014 Vladimir Angelov. All rights reserved.

//



#import <Foundation/Foundation.h>



@class VLDContextSheet;

@class VLDContextSheetItem;



@protocol VLDContextSheetDelegate <NSObject>



- (void) contextSheet: (VLDContextSheet *) contextSheet didSelectItem: (VLDContextSheetItem *) item;



@end



@interface VLDContextSheet : UIView



@property (assign, nonatomic) NSInteger radius;

@property (assign, nonatomic) CGFloat rotation;

@property (assign, nonatomic) CGFloat rangeAngle;

@property (strong, nonatomic) NSArray *items;

@property (assign, nonatomic) id<VLDContextSheetDelegate> delegate;



- (id) initWithItems: (NSArray *) items;



- (void) startWithGestureRecognizer: (UIGestureRecognizer *) gestureRecognizer

                             inView: (UIView *) view;

- (void) end;



@end
//

//  VLDContextSheet.m

//

//  Created by Vladimir Angelov on 2/7/14.

//  Copyright (c) 2014 Vladimir Angelov. All rights reserved.

//



#import "VLDContextSheetItemView.h"

#import "VLDContextSheet.h"



typedef struct {

    CGRect rect;

    CGFloat rotation;

} VLDZone;



static const NSInteger VLDMaxTouchDistanceAllowance = 40;

static const NSInteger VLDZonesCount = 10;



static inline VLDZone VLDZoneMake(CGRect rect, CGFloat rotation) {

    VLDZone zone;

    

    zone.rect = rect;

    zone.rotation = rotation;

    

    return zone;

}



static CGFloat VLDVectorDotProduct(CGPoint vector1, CGPoint vector2) {

    return vector1.x * vector2.x + vector1.y * vector2.y;

}



static CGFloat VLDVectorLength(CGPoint vector) {

    return sqrt(vector.x * vector.x + vector.y * vector.y);

}



static CGRect VLDOrientedScreenBounds() {

    CGRect bounds = [UIScreen mainScreen].bounds;

    

    if(UIInterfaceOrientationIsLandscape([UIApplication sharedApplication].statusBarOrientation) &&

        bounds.size.width < bounds.size.height) {

        

        bounds.size = CGSizeMake(bounds.size.height, bounds.size.width);

    }

    

    return bounds;

}



@interface VLDContextSheet ()



@property (strong, nonatomic) NSArray *itemViews;

@property (strong, nonatomic) UIView *centerView;

@property (strong, nonatomic) UIView *backgroundView;

@property (strong, nonatomic) VLDContextSheetItemView *selectedItemView;

@property (assign, nonatomic) BOOL openAnimationFinished;

@property (assign, nonatomic) CGPoint touchCenter;

@property (strong, nonatomic) UIGestureRecognizer *starterGestureRecognizer;



@end



@implementation VLDContextSheet {

    

    VLDZone zones[VLDZonesCount];

}



- (id) initWithFrame: (CGRect) frame {

    return [self initWithItems: nil];

}



- (id) initWithItems: (NSArray *) items {

    self = [super initWithFrame: VLDOrientedScreenBounds()];

    

    if(self) {

        _items = items;

        _radius = 100;

        _rangeAngle = M_PI / 1.6;

        

        [self createSubviews];

    }

    

    return self;

}



- (void) dealloc {

    [self.starterGestureRecognizer removeTarget: self action: @selector(gestureRecognizedStateObserver:)];

}



- (void) createSubviews {

    _backgroundView = [[UIView alloc] initWithFrame: CGRectZero];

    _backgroundView.backgroundColor = [UIColor colorWithWhite: 0 alpha: 0.6];

    [self addSubview: self.backgroundView];

    

    _itemViews = [[NSMutableArray alloc] init];

    

    for(VLDContextSheetItem *item in _items) {

        VLDContextSheetItemView *itemView = [[VLDContextSheetItemView alloc] init];

        itemView.item = item;

        

        [self addSubview: itemView];

        [(NSMutableArray *) _itemViews addObject: itemView];

    }

    

    VLDContextSheetItemView *sampleItemView = _itemViews[0];

    

    _centerView = [[UIView alloc] initWithFrame: CGRectMake(0, 0, sampleItemView.frame.size.width, sampleItemView.frame.size.width)];

    _centerView.layer.cornerRadius = 25;

    _centerView.layer.borderWidth = 2;

    _centerView.layer.borderColor = [UIColor grayColor].CGColor;

    [self addSubview: _centerView];



}



- (void) layoutSubviews {

    [super layoutSubviews];

        

    self.backgroundView.frame = self.bounds;

}



- (void) setCenterViewHighlighted: (BOOL) highlighted {

    _centerView.backgroundColor = highlighted ? [UIColor colorWithWhite: 0.5 alpha: 0.4] : nil;

}



- (void) createZones {

    CGRect screenRect = self.bounds;

    

    NSInteger rowHeight1 = 120;

    

    zones[0] = VLDZoneMake(CGRectMake(0, 0, 70, rowHeight1), 0.8);

    zones[1] = VLDZoneMake(CGRectMake(zones[0].rect.size.width, 0, 40, rowHeight1), 0.4);

    

    zones[2] = VLDZoneMake(CGRectMake(zones[1].rect.origin.x + zones[1].rect.size.width, 0, screenRect.size.width - 2 *(zones[0].rect.size.width + zones[1].rect.size.width), rowHeight1), 0);

    

    zones[3] = VLDZoneMake(CGRectMake(zones[2].rect.origin.x + zones[2].rect.size.width, 0, zones[1].rect.size.width, rowHeight1),  -zones[1].rotation);

    zones[4] = VLDZoneMake(CGRectMake(zones[3].rect.origin.x + zones[3].rect.size.width, 0, zones[0].rect.size.width, rowHeight1), -zones[0].rotation);

    

    NSInteger rowHeight2 = screenRect.size.height - zones[0].rect.size.height;

    

    zones[5] = VLDZoneMake(CGRectMake(0, zones[0].rect.size.height, zones[0].rect.size.width, rowHeight2), M_PI - zones[0].rotation);

    zones[6] = VLDZoneMake(CGRectMake(zones[5].rect.size.width, zones[5].rect.origin.y, zones[1].rect.size.width, rowHeight2), M_PI - zones[1].rotation);

    zones[7] = VLDZoneMake(CGRectMake(zones[6].rect.origin.x + zones[6].rect.size.width, zones[5].rect.origin.y, zones[2].rect.size.width, rowHeight2), M_PI - zones[2].rotation);

    zones[8] = VLDZoneMake(CGRectMake(zones[7].rect.origin.x + zones[7].rect.size.width, zones[5].rect.origin.y, zones[3].rect.size.width, rowHeight2), M_PI - zones[3].rotation);

    zones[9] = VLDZoneMake(CGRectMake(zones[8].rect.origin.x + zones[8].rect.size.width, zones[5].rect.origin.y, zones[4].rect.size.width, rowHeight2), M_PI - zones[4].rotation);

}



/* Only used for testing the touch zones */

- (void) drawZones {

    for(int i = 0; i < VLDZonesCount; i++) {

        UIView *zoneView = [[UIView alloc] initWithFrame: zones[i].rect];

        

        CGFloat hue = ( arc4random() % 256 / 256.0 );

        CGFloat saturation = ( arc4random() % 128 / 256.0 ) + 0.5;

        CGFloat brightness = ( arc4random() % 128 / 256.0 ) + 0.5;

        UIColor *color = [UIColor colorWithHue:hue saturation:saturation brightness:brightness alpha:1];

        

        zoneView.backgroundColor = color;

        [self addSubview: zoneView];

    }

}



- (void) updateItemView: (UIView *) itemView

          touchDistance: (CGFloat) touchDistance

               animated: (BOOL) animated  {

    

    if(!animated) {

        [self updateItemViewNotAnimated: itemView touchDistance: touchDistance];

    }

    else  {        

        [UIView animateWithDuration: 0.4

                              delay: 0

             usingSpringWithDamping: 0.45

              initialSpringVelocity: 7.5

                            options: UIViewAnimationOptionBeginFromCurrentState

                         animations: ^{

                             [self updateItemViewNotAnimated: itemView

                                               touchDistance: touchDistance];

                         }

                         completion: nil];

    }

}



- (void) updateItemViewNotAnimated: (UIView *) itemView touchDistance: (CGFloat) touchDistance  {

    NSInteger itemIndex = [self.itemViews indexOfObject: itemView];

    CGFloat angle = -0.65 + self.rotation + itemIndex * (self.rangeAngle / self.itemViews.count);

    

    CGFloat resistanceFactor = 1.0 / (touchDistance > 0 ? 6.0 : 3.0);

    

    itemView.center = CGPointMake(self.touchCenter.x + (self.radius + touchDistance * resistanceFactor) * sin(angle),

                                  self.touchCenter.y + (self.radius + touchDistance * resistanceFactor) * cos(angle));

    

    CGFloat scale = 1 + 0.2 * (fabs(touchDistance) / self.radius);

    

    itemView.transform = CGAffineTransformMakeScale(scale, scale);

}



- (void) openItemsFromCenterView {

    self.openAnimationFinished = NO;

    

    for(int i = 0; i < self.itemViews.count; i++) {

        VLDContextSheetItemView *itemView = self.itemViews[i];

        itemView.transform = CGAffineTransformIdentity;

        itemView.center = self.touchCenter;

        [itemView setHighlighted: NO animated: NO];

        

        [UIView animateWithDuration: 0.5

                              delay: i * 0.01

             usingSpringWithDamping: 0.45

              initialSpringVelocity: 7.5

                            options: 0

                         animations: ^{

                             [self updateItemViewNotAnimated: itemView touchDistance: 0.0];

                             

                         }

                         completion: ^(BOOL finished) {

                             self.openAnimationFinished = YES;

                         }];

    }

}



- (void) closeItemsToCenterView {

    [UIView animateWithDuration: 0.1

                          delay: 0.0

                        options: UIViewAnimationOptionCurveEaseInOut

                     animations:^{

                         self.alpha = 0.0;

                     }

                     completion:^(BOOL finished) {

                         [self removeFromSuperview];

                         self.alpha = 1.0;

                     }];

    

}



- (void) startWithGestureRecognizer: (UIGestureRecognizer *) gestureRecognizer inView: (UIView *) view {

    [view addSubview: self];

    

    self.frame = VLDOrientedScreenBounds();

    [self createZones];

    

    self.starterGestureRecognizer = gestureRecognizer;

    

    self.touchCenter = [self.starterGestureRecognizer locationInView: self];

    self.centerView.center = self.touchCenter;

    self.selectedItemView = nil;

    [self setCenterViewHighlighted: YES];

    self.rotation = [self rotationForCenter: self.centerView.center];

    

    [self openItemsFromCenterView];

    

    [self.starterGestureRecognizer addTarget: self action: @selector(gestureRecognizedStateObserver:)];

}



- (CGFloat) rotationForCenter: (CGPoint) center {

    for(NSInteger i = 0; i < 10; i++) {

        VLDZone zone = zones[i];

        

        if(CGRectContainsPoint(zone.rect, center)) {

            return zone.rotation;

        }

    }

    

    return 0;

}



- (void) gestureRecognizedStateObserver: (UIGestureRecognizer *) gestureRecognizer {

    if(self.openAnimationFinished && gestureRecognizer.state == UIGestureRecognizerStateChanged) {

        CGPoint touchPoint = [gestureRecognizer locationInView: self];

        

        [self updateItemViewsForTouchPoint: touchPoint];

    }

    else if(gestureRecognizer.state == UIGestureRecognizerStateEnded || gestureRecognizer.state == UIGestureRecognizerStateCancelled) {

        if(gestureRecognizer.state == UIGestureRecognizerStateCancelled) {

            self.selectedItemView = nil;

        }

        

        [self end];

    }

}



- (CGFloat) signedTouchDistanceForTouchVector: (CGPoint) touchVector itemView: (UIView *) itemView {

    CGFloat touchDistance = VLDVectorLength(touchVector);

    

    CGPoint oldCenter = itemView.center;

    CGAffineTransform oldTransform = itemView.transform;

    

    [self updateItemViewNotAnimated: itemView touchDistance: self.radius + 40];

    

    if(!CGRectContainsRect(self.bounds, itemView.frame)) {

        touchDistance = -touchDistance;

    }

    

    itemView.center = oldCenter;

    itemView.transform = oldTransform;

    

    return touchDistance;

}



- (void) updateItemViewsForTouchPoint: (CGPoint) touchPoint {

    CGPoint touchVector = {touchPoint.x - self.touchCenter.x, touchPoint.y - self.touchCenter.y};

    VLDContextSheetItemView *itemView = [self itemViewForTouchVector: touchVector];

    CGFloat touchDistance = [self signedTouchDistanceForTouchVector: touchVector itemView: itemView];

    

    if(fabs(touchDistance) <= VLDMaxTouchDistanceAllowance) {

        self.centerView.center = CGPointMake(self.touchCenter.x + touchVector.x, self.touchCenter.y + touchVector.y);

        [self setCenterViewHighlighted: YES];

    }

    else {

        [self setCenterViewHighlighted: NO];

        

        [UIView animateWithDuration: 0.4

                              delay: 0

             usingSpringWithDamping: 0.35

              initialSpringVelocity: 7.5

                            options: UIViewAnimationOptionBeginFromCurrentState

                         animations: ^{

                             self.centerView.center = self.touchCenter;

                             

                         }

                         completion: nil];

    }

    

    if(touchDistance > self.radius + VLDMaxTouchDistanceAllowance) {

        [itemView setHighlighted: NO animated: YES];

        

        [self updateItemView: itemView

               touchDistance: 0.0

                    animated: YES];

        

        self.selectedItemView = nil;

        

        return;

    }

    

    if(itemView != self.selectedItemView) {

        [self.selectedItemView setHighlighted: NO animated: YES];

        

        [self updateItemView: self.selectedItemView

               touchDistance: 0.0

                    animated: YES];

        

        [self updateItemView: itemView

               touchDistance: touchDistance

                    animated: YES];

        

        [self bringSubviewToFront: itemView];

    }

    else  {

        [self updateItemView: itemView

               touchDistance: touchDistance

                    animated: NO];

    }

    

    if(fabs(touchDistance) > VLDMaxTouchDistanceAllowance) {

        [itemView setHighlighted: YES animated: YES];

    }

    

    self.selectedItemView = itemView;

}



- (VLDContextSheetItemView *) itemViewForTouchVector: (CGPoint) touchVector  {

    CGFloat maxCosOfAngle = -2;

    VLDContextSheetItemView *resultItemView = nil;

    

    for(int i = 0; i < self.itemViews.count; i++) {

        VLDContextSheetItemView *itemView = self.itemViews[i];

        CGPoint itemViewVector = {

            itemView.center.x - self.touchCenter.x,

            itemView.center.y - self.touchCenter.y

        };

        

        CGFloat cosOfAngle = VLDVectorDotProduct(itemViewVector, touchVector) / VLDVectorLength(itemViewVector);

        

        if(cosOfAngle > maxCosOfAngle) {

            maxCosOfAngle = cosOfAngle;

            resultItemView = itemView;

        }

    }



    return resultItemView;

}



- (void) end {

    [self.starterGestureRecognizer removeTarget: self action: @selector(gestureRecognizedStateObserver:)];

    

    if(self.selectedItemView && self.selectedItemView.isHighlighted) {

        [self.delegate contextSheet: self didSelectItem: self.selectedItemView.item];

    }

    

    [self closeItemsToCenterView];

}



@end

控制器源码:

//

//  VLDExampleViewController.h

//  VLDContextSheetExample

//

//  Created by Vladimir Angelov on 11/2/14.

//  Copyright (c) 2014 Vladimir Angelov. All rights reserved.

//



#import <Foundation/Foundation.h>

#import "VLDContextSheet.h"



@interface VLDExampleViewController : UIViewController <VLDContextSheetDelegate>



@property (strong, nonatomic) VLDContextSheet *contextSheet;



@end
//

//  VLDExampleViewController.m

//  VLDContextSheetExample

//

//  Created by Vladimir Angelov on 11/2/14.

//  Copyright (c) 2014 Vladimir Angelov. All rights reserved.

//



#import "VLDExampleViewController.h"

#import "VLDContextSheetItem.h"





@implementation VLDExampleViewController



- (void) viewDidLoad {

    [super viewDidLoad];

    [self createContextSheet];

    

    self.view.backgroundColor = [UIColor blackColor];

    

    UIGestureRecognizer *gestureRecognizer = [[UILongPressGestureRecognizer alloc] initWithTarget: self

                                                                                    action: @selector(longPressed:)];

    [self.view addGestureRecognizer: gestureRecognizer];

}



- (void) createContextSheet {

    VLDContextSheetItem *item1 = [[VLDContextSheetItem alloc] initWithTitle: @"Gift"

                                                                image: [UIImage imageNamed: @"gift"]

                                                     highlightedImage: [UIImage imageNamed: @"gift_highlighted"]];



    

    VLDContextSheetItem *item2 = [[VLDContextSheetItem alloc] initWithTitle: @"Add to"

                                                                image: [UIImage imageNamed: @"add"]

                                                     highlightedImage: [UIImage imageNamed: @"add_highlighted"]];

    

    VLDContextSheetItem *item3 = [[VLDContextSheetItem alloc] initWithTitle: @"Share"

                                                                image: [UIImage imageNamed: @"share"]

                                                     highlightedImage: [UIImage imageNamed: @"share_highlighted"]];

    

    self.contextSheet = [[VLDContextSheet alloc] initWithItems: @[ item1, item2, item3 ]];

    self.contextSheet.delegate = self;

}



- (void) contextSheet: (VLDContextSheet *) contextSheet didSelectItem: (VLDContextSheetItem *) item {

    NSLog(@"Selected item: %@", item.title);

}



- (void) longPressed: (UIGestureRecognizer *) gestureRecognizer {

    if(gestureRecognizer.state == UIGestureRecognizerStateBegan) {



        [self.contextSheet startWithGestureRecognizer: gestureRecognizer

                                               inView: self.view];

    }

}



- (void) willRotateToInterfaceOrientation: (UIInterfaceOrientation) toInterfaceOrientation

                                 duration: (NSTimeInterval) duration {

    

    [super willRotateToInterfaceOrientation: toInterfaceOrientation duration: duration];



    [self.contextSheet end];

}



@end

 

你可能感兴趣的:(context)