├── Source ├── demo0.gif ├── demo1.gif └── demo3.gif ├── DGGooeySlide ObjC ├── DGGooeySlide │ ├── Assets.xcassets │ │ ├── Contents.json │ │ ├── img.imageset │ │ │ ├── t0141b2bb3491466951.png │ │ │ └── Contents.json │ │ └── AppIcon.appiconset │ │ │ └── Contents.json │ ├── ViewController.h │ ├── AppDelegate.h │ ├── main.m │ ├── DGGooeySlideMenu.h │ ├── ViewController.m │ ├── Info.plist │ ├── Base.lproj │ │ ├── LaunchScreen.storyboard │ │ └── Main.storyboard │ ├── AppDelegate.m │ └── DGGooeySlideMenu.m └── DGGooeySlide.xcodeproj │ ├── xcuserdata │ └── Desgard_Duan.xcuserdatad │ │ ├── xcdebugger │ │ └── Breakpoints_v2.xcbkptlist │ │ └── xcschemes │ │ ├── xcschememanagement.plist │ │ └── DGGooeySlide.xcscheme │ ├── project.xcworkspace │ ├── contents.xcworkspacedata │ └── xcuserdata │ │ └── Desgard_Duan.xcuserdatad │ │ └── UserInterfaceState.xcuserstate │ └── project.pbxproj └── README.md /Source/demo0.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Desgard/DGGooeySlideMenu/HEAD/Source/demo0.gif -------------------------------------------------------------------------------- /Source/demo1.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Desgard/DGGooeySlideMenu/HEAD/Source/demo1.gif -------------------------------------------------------------------------------- /Source/demo3.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Desgard/DGGooeySlideMenu/HEAD/Source/demo3.gif -------------------------------------------------------------------------------- /DGGooeySlide ObjC/DGGooeySlide/Assets.xcassets/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "info" : { 3 | "version" : 1, 4 | "author" : "xcode" 5 | } 6 | } -------------------------------------------------------------------------------- /DGGooeySlide ObjC/DGGooeySlide.xcodeproj/xcuserdata/Desgard_Duan.xcuserdatad/xcdebugger/Breakpoints_v2.xcbkptlist: -------------------------------------------------------------------------------- 1 | 2 | 5 | 6 | -------------------------------------------------------------------------------- /DGGooeySlide ObjC/DGGooeySlide/Assets.xcassets/img.imageset/t0141b2bb3491466951.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Desgard/DGGooeySlideMenu/HEAD/DGGooeySlide ObjC/DGGooeySlide/Assets.xcassets/img.imageset/t0141b2bb3491466951.png -------------------------------------------------------------------------------- /DGGooeySlide ObjC/DGGooeySlide.xcodeproj/project.xcworkspace/contents.xcworkspacedata: -------------------------------------------------------------------------------- 1 | 2 | 4 | 6 | 7 | 8 | -------------------------------------------------------------------------------- /DGGooeySlide ObjC/DGGooeySlide.xcodeproj/project.xcworkspace/xcuserdata/Desgard_Duan.xcuserdatad/UserInterfaceState.xcuserstate: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Desgard/DGGooeySlideMenu/HEAD/DGGooeySlide ObjC/DGGooeySlide.xcodeproj/project.xcworkspace/xcuserdata/Desgard_Duan.xcuserdatad/UserInterfaceState.xcuserstate -------------------------------------------------------------------------------- /DGGooeySlide ObjC/DGGooeySlide/ViewController.h: -------------------------------------------------------------------------------- 1 | // 2 | // ViewController.h 3 | // DGGooeySlide 4 | // 5 | // Created by 段昊宇 on 16/6/4. 6 | // Copyright © 2016年 Desgard_Duan. All rights reserved. 7 | // 8 | 9 | #import 10 | 11 | @interface ViewController : UIViewController 12 | 13 | 14 | @end 15 | 16 | -------------------------------------------------------------------------------- /DGGooeySlide ObjC/DGGooeySlide/AppDelegate.h: -------------------------------------------------------------------------------- 1 | // 2 | // AppDelegate.h 3 | // DGGooeySlide 4 | // 5 | // Created by 段昊宇 on 16/6/4. 6 | // Copyright © 2016年 Desgard_Duan. All rights reserved. 7 | // 8 | 9 | #import 10 | 11 | @interface AppDelegate : UIResponder 12 | 13 | @property (strong, nonatomic) UIWindow *window; 14 | 15 | 16 | @end 17 | 18 | -------------------------------------------------------------------------------- /DGGooeySlide ObjC/DGGooeySlide/main.m: -------------------------------------------------------------------------------- 1 | // 2 | // main.m 3 | // DGGooeySlide 4 | // 5 | // Created by 段昊宇 on 16/6/4. 6 | // Copyright © 2016年 Desgard_Duan. All rights reserved. 7 | // 8 | 9 | #import 10 | #import "AppDelegate.h" 11 | 12 | int main(int argc, char * argv[]) { 13 | @autoreleasepool { 14 | return UIApplicationMain(argc, argv, nil, NSStringFromClass([AppDelegate class])); 15 | } 16 | } 17 | -------------------------------------------------------------------------------- /DGGooeySlide ObjC/DGGooeySlide/Assets.xcassets/img.imageset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "idiom" : "universal", 5 | "scale" : "1x" 6 | }, 7 | { 8 | "idiom" : "universal", 9 | "filename" : "t0141b2bb3491466951.png", 10 | "scale" : "2x" 11 | }, 12 | { 13 | "idiom" : "universal", 14 | "scale" : "3x" 15 | } 16 | ], 17 | "info" : { 18 | "version" : 1, 19 | "author" : "xcode" 20 | } 21 | } -------------------------------------------------------------------------------- /DGGooeySlide ObjC/DGGooeySlide/DGGooeySlideMenu.h: -------------------------------------------------------------------------------- 1 | // 2 | // DGGooeySlideMenu.h 3 | // DGGooeySlide 4 | // 5 | // Created by 段昊宇 on 16/6/4. 6 | // Copyright © 2016年 Desgard_Duan. All rights reserved. 7 | // 8 | 9 | #import 10 | 11 | @interface DGGooeySlideMenu : UIView 12 | 13 | 14 | 15 | -(id)initWithTitles:(NSArray *)titles; 16 | 17 | -(id)initWithTitles:(NSArray *)titles withButtonHeight:(CGFloat)height withMenuColor:(UIColor *)menuColor withBackBlurStyle:(UIBlurEffectStyle)style; 18 | 19 | -(void)trigger; 20 | 21 | 22 | @end 23 | -------------------------------------------------------------------------------- /DGGooeySlide ObjC/DGGooeySlide.xcodeproj/xcuserdata/Desgard_Duan.xcuserdatad/xcschemes/xcschememanagement.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | SchemeUserState 6 | 7 | DGGooeySlide.xcscheme 8 | 9 | orderHint 10 | 0 11 | 12 | 13 | SuppressBuildableAutocreation 14 | 15 | D53F27D31D0252E900A0704C 16 | 17 | primary 18 | 19 | 20 | 21 | 22 | 23 | -------------------------------------------------------------------------------- /DGGooeySlide ObjC/DGGooeySlide/ViewController.m: -------------------------------------------------------------------------------- 1 | // 2 | // ViewController.m 3 | // DGGooeySlide 4 | // 5 | // Created by 段昊宇 on 16/6/4. 6 | // Copyright © 2016年 Desgard_Duan. All rights reserved. 7 | // 8 | 9 | #import "ViewController.h" 10 | #import "DGGooeySlideMenu.h" 11 | 12 | @interface ViewController () 13 | 14 | @property (nonatomic, strong) DGGooeySlideMenu *menu; 15 | 16 | @end 17 | 18 | @implementation ViewController 19 | 20 | - (void)viewDidLoad { 21 | [super viewDidLoad]; 22 | self.menu = [[DGGooeySlideMenu alloc] initWithTitles:@[@"123", @"123" ]]; 23 | 24 | } 25 | 26 | - (IBAction)clickButton:(id)sender { 27 | [self.menu trigger]; 28 | } 29 | @end 30 | -------------------------------------------------------------------------------- /DGGooeySlide ObjC/DGGooeySlide/Assets.xcassets/AppIcon.appiconset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "idiom" : "iphone", 5 | "size" : "29x29", 6 | "scale" : "2x" 7 | }, 8 | { 9 | "idiom" : "iphone", 10 | "size" : "29x29", 11 | "scale" : "3x" 12 | }, 13 | { 14 | "idiom" : "iphone", 15 | "size" : "40x40", 16 | "scale" : "2x" 17 | }, 18 | { 19 | "idiom" : "iphone", 20 | "size" : "40x40", 21 | "scale" : "3x" 22 | }, 23 | { 24 | "idiom" : "iphone", 25 | "size" : "60x60", 26 | "scale" : "2x" 27 | }, 28 | { 29 | "idiom" : "iphone", 30 | "size" : "60x60", 31 | "scale" : "3x" 32 | } 33 | ], 34 | "info" : { 35 | "version" : 1, 36 | "author" : "xcode" 37 | } 38 | } -------------------------------------------------------------------------------- /DGGooeySlide ObjC/DGGooeySlide/Info.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | CFBundleDevelopmentRegion 6 | en 7 | CFBundleExecutable 8 | $(EXECUTABLE_NAME) 9 | CFBundleIdentifier 10 | $(PRODUCT_BUNDLE_IDENTIFIER) 11 | CFBundleInfoDictionaryVersion 12 | 6.0 13 | CFBundleName 14 | $(PRODUCT_NAME) 15 | CFBundlePackageType 16 | APPL 17 | CFBundleShortVersionString 18 | 1.0 19 | CFBundleSignature 20 | ???? 21 | CFBundleVersion 22 | 1 23 | LSRequiresIPhoneOS 24 | 25 | UILaunchStoryboardName 26 | LaunchScreen 27 | UIMainStoryboardFile 28 | Main 29 | UIRequiredDeviceCapabilities 30 | 31 | armv7 32 | 33 | UISupportedInterfaceOrientations 34 | 35 | UIInterfaceOrientationPortrait 36 | UIInterfaceOrientationLandscapeLeft 37 | UIInterfaceOrientationLandscapeRight 38 | 39 | 40 | 41 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # DGGooeySlideMenu 2 | 3 | ![Platforms](https://cocoapod-badges.herokuapp.com/p/MZTimerLabel/badge.png) 4 | 5 | DGGooeySlideMenu---模仿skype照相按钮弹出菜单弹簧效果 6 | 7 | 制作思路及代码解释可查看博文[Recreating Skype's Action Sheet Animation](http://desgard.com/2016/06/05/DGGooeySlideMenu/) 8 | 9 | img 10 | 11 | ## 计算弹性数组序列 12 | 13 | 先要确定我们的目标:**构造一个连续序列,这个序列的末状态是0,过程中先增大,再减小,再增大……重复以上过程,因为阻尼衰减,到最后会停留在0,则序列结束。**这个连续序列就好比缓动函数中的[EaseOutElastic](http://www.xuanfengge.com/easeing/easeing/#easeOutElastic)。 14 | 15 | 在iOS7之后,Apple在[UIView Class Refference](https://developer.apple.com/library/ios/documentation/UIKit/Reference/UIView_Class/#//apple_ref/occ/clm/UIView/animateWithDuration:delay:usingSpringWithDamping:initialSpringVelocity:options:animations:completion:)增加了弹簧动画效果。 16 | 17 | ```Objective-C 18 | + (void)animateWithDuration: (NSTimeInterval)duration 19 | delay: (NSTimeInterval)delay 20 | usingSpringWithDamping: (CGFloat)dampingRatio 21 | initialSpringVelocity: (CGFloat)velocity 22 | options: (UIViewAnimationOptions)options 23 | animations: (void (^)(void))animations 24 | completion: (void (^)(BOOL finished))completion 25 | ``` 26 | 27 | 我们的灵感来自于官方的这个函数。这里在构造序列的时候,**通过两个视图在不同的时间内执行弹簧动画**,即可得到我们所需要的序列(文字说的不明白,可以看我录制图)。这种方法在Kitten-Yang的书中第二章也详细的介绍了,被称作**辅助视图(Side Helper View)**法。这里我把效果放慢,大家观察两个不同颜色的Rect在Y轴上的距离变化: 28 | 29 | ![](/Source/demo3.gif) 30 | 31 | ## 后续任务 32 | 33 | * 根据弹簧效果的两个rect视图,计算弹簧序列。(`diff`序列) 【已完成】 34 | * 重写`drawRect`函数,增加`runtime`频率刷新贝塞尔曲线视图。【已完成】 35 | * 给出initWithTitles接口,传入多个button的Title 36 | * 完成button的布局 37 | -------------------------------------------------------------------------------- /DGGooeySlide ObjC/DGGooeySlide/Base.lproj/LaunchScreen.storyboard: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | -------------------------------------------------------------------------------- /DGGooeySlide ObjC/DGGooeySlide/AppDelegate.m: -------------------------------------------------------------------------------- 1 | // 2 | // AppDelegate.m 3 | // DGGooeySlide 4 | // 5 | // Created by 段昊宇 on 16/6/4. 6 | // Copyright © 2016年 Desgard_Duan. All rights reserved. 7 | // 8 | 9 | #import "AppDelegate.h" 10 | 11 | @interface AppDelegate () 12 | 13 | @end 14 | 15 | @implementation AppDelegate 16 | 17 | 18 | - (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions { 19 | // Override point for customization after application launch. 20 | return YES; 21 | } 22 | 23 | - (void)applicationWillResignActive:(UIApplication *)application { 24 | // Sent when the application is about to move from active to inactive state. This can occur for certain types of temporary interruptions (such as an incoming phone call or SMS message) or when the user quits the application and it begins the transition to the background state. 25 | // Use this method to pause ongoing tasks, disable timers, and throttle down OpenGL ES frame rates. Games should use this method to pause the game. 26 | } 27 | 28 | - (void)applicationDidEnterBackground:(UIApplication *)application { 29 | // Use this method to release shared resources, save user data, invalidate timers, and store enough application state information to restore your application to its current state in case it is terminated later. 30 | // If your application supports background execution, this method is called instead of applicationWillTerminate: when the user quits. 31 | } 32 | 33 | - (void)applicationWillEnterForeground:(UIApplication *)application { 34 | // Called as part of the transition from the background to the inactive state; here you can undo many of the changes made on entering the background. 35 | } 36 | 37 | - (void)applicationDidBecomeActive:(UIApplication *)application { 38 | // Restart any tasks that were paused (or not yet started) while the application was inactive. If the application was previously in the background, optionally refresh the user interface. 39 | } 40 | 41 | - (void)applicationWillTerminate:(UIApplication *)application { 42 | // Called when the application is about to terminate. Save data if appropriate. See also applicationDidEnterBackground:. 43 | } 44 | 45 | @end 46 | -------------------------------------------------------------------------------- /DGGooeySlide ObjC/DGGooeySlide.xcodeproj/xcuserdata/Desgard_Duan.xcuserdatad/xcschemes/DGGooeySlide.xcscheme: -------------------------------------------------------------------------------- 1 | 2 | 5 | 8 | 9 | 15 | 21 | 22 | 23 | 24 | 25 | 30 | 31 | 32 | 33 | 39 | 40 | 41 | 42 | 43 | 44 | 54 | 56 | 62 | 63 | 64 | 65 | 66 | 67 | 73 | 75 | 81 | 82 | 83 | 84 | 86 | 87 | 90 | 91 | 92 | -------------------------------------------------------------------------------- /DGGooeySlide ObjC/DGGooeySlide/Base.lproj/Main.storyboard: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | 48 | 49 | 50 | 51 | 52 | 53 | 54 | 55 | 56 | 57 | 58 | 59 | 60 | 61 | 62 | 63 | 64 | 65 | 66 | 67 | -------------------------------------------------------------------------------- /DGGooeySlide ObjC/DGGooeySlide/DGGooeySlideMenu.m: -------------------------------------------------------------------------------- 1 | // 2 | // DGGooeySlideMenu.m 3 | // DGGooeySlide 4 | // 5 | // Created by 段昊宇 on 16/6/4. 6 | // Copyright © 2016年 Desgard_Duan. All rights reserved. 7 | // 8 | 9 | #import "DGGooeySlideMenu.h" 10 | 11 | #define buttonSpace 30 12 | #define menuBlankWidth 80 13 | #define wid [UIScreen mainScreen].bounds.size.width 14 | #define hei [UIScreen mainScreen].bounds.size.height 15 | #define kwid keyWindow.frame.size.width 16 | #define khei keyWindow.frame.size.height 17 | #define swid self.frame.size.width 18 | #define shei self.frame.size.height 19 | 20 | @interface DGGooeySlideMenu() { 21 | UIVisualEffectView *blurView; 22 | UIView *helperSideView; 23 | UIView *helperCenterView; 24 | UIWindow *keyWindow; 25 | BOOL triggered; 26 | CGFloat diff; 27 | UIColor *_menuColor; 28 | CGFloat menuButtonHeight; 29 | } 30 | 31 | @property (nonatomic,strong) CADisplayLink *displayLink; 32 | @property NSInteger animationCount; // 动画的数量 33 | 34 | @end 35 | 36 | @implementation DGGooeySlideMenu 37 | 38 | #pragma mark - Overite 39 | -(id)initWithTitles:(NSArray *)titles{ 40 | return [self initWithTitles:titles withButtonHeight:40.0f withMenuColor: [UIColor colorWithRed:0 green:175 / 255.f blue: 240 / 255.f alpha:1] withBackBlurStyle:UIBlurEffectStyleDark]; 41 | } 42 | 43 | -(id)initWithTitles: (NSArray *)titles withButtonHeight: (CGFloat)height withMenuColor: (UIColor *)menuColor withBackBlurStyle: (UIBlurEffectStyle) style { 44 | 45 | self = [super init]; 46 | if (self) { 47 | keyWindow = [[UIApplication sharedApplication] keyWindow]; 48 | 49 | // 背景设置为模糊效果 50 | // UIVisualEffectView 51 | blurView = [[UIVisualEffectView alloc]initWithEffect:[UIBlurEffect effectWithStyle:style]]; 52 | blurView.frame = keyWindow.frame; 53 | blurView.alpha = 0.0f; 54 | 55 | // 左下角辅助视图 56 | helperSideView = [[UIView alloc] initWithFrame: CGRectMake(0, hei + 40, 40, 40)]; 57 | helperSideView.backgroundColor = [UIColor redColor]; 58 | helperSideView.hidden = YES; 59 | [keyWindow addSubview: helperSideView]; 60 | 61 | // 中央辅助视图 62 | helperCenterView = [[UIView alloc] initWithFrame: CGRectMake(wid / 2 - 20, hei + 40, 40, 40)]; 63 | helperCenterView.backgroundColor = [UIColor yellowColor]; 64 | helperCenterView.hidden = YES; 65 | [keyWindow addSubview: helperCenterView]; 66 | 67 | // 创建下边界界外的视图 68 | self.frame = CGRectMake(0, khei + khei / 2 + menuBlankWidth, kwid, khei / 2 + menuBlankWidth); 69 | self.backgroundColor = [UIColor clearColor]; 70 | [keyWindow insertSubview: self belowSubview: helperSideView]; 71 | 72 | _menuColor = menuColor; 73 | menuButtonHeight = height; 74 | 75 | // 视图辅助观察颜色 76 | // self.backgroundColor = [UIColor redColor]; 77 | [self addButton]; 78 | } 79 | return self; 80 | } 81 | 82 | - (void) addButton { 83 | 84 | } 85 | 86 | - (void) drawRect:(CGRect)rect { 87 | UIBezierPath *path = [UIBezierPath bezierPath]; 88 | [path moveToPoint: CGPointMake(0, shei)]; 89 | [path addLineToPoint: CGPointMake(0, shei - khei / 2 )]; 90 | [path addQuadCurveToPoint: CGPointMake(wid, shei - khei / 2) 91 | controlPoint: CGPointMake(swid / 2, diff + menuBlankWidth)]; 92 | [path addLineToPoint: CGPointMake(wid, shei)]; 93 | [path closePath]; 94 | 95 | CGContextRef context = UIGraphicsGetCurrentContext(); 96 | CGContextAddPath(context, path.CGPath); 97 | [_menuColor set]; 98 | CGContextFillPath(context); 99 | } 100 | 101 | 102 | - (void) trigger{ 103 | if (!triggered) { 104 | [keyWindow insertSubview: blurView belowSubview:self]; 105 | [UIView animateWithDuration: 0.618 animations:^{ 106 | self.frame = CGRectMake(0, hei / 2 - menuBlankWidth, wid, hei / 2 + menuBlankWidth); 107 | }]; 108 | 109 | [self beforeAnimation]; 110 | [UIView animateWithDuration: 1 111 | delay: 0.0f 112 | usingSpringWithDamping: 0.5f 113 | initialSpringVelocity: 0.9f 114 | options: UIViewAnimationOptionBeginFromCurrentState | UIViewAnimationOptionAllowUserInteraction 115 | animations: ^{ 116 | helperSideView.center = CGPointMake(20, hei / 2); 117 | } 118 | completion: ^(BOOL finished) { 119 | [self finishAnimation]; 120 | }]; 121 | 122 | [UIView animateWithDuration: 0.3 animations: ^{ 123 | blurView.alpha = 1.0f; 124 | }]; 125 | 126 | [self beforeAnimation]; 127 | [UIView animateWithDuration: 1 128 | delay: 0.0f 129 | usingSpringWithDamping: 0.8f 130 | initialSpringVelocity: 2.0f 131 | options: UIViewAnimationOptionBeginFromCurrentState | UIViewAnimationOptionAllowUserInteraction 132 | animations: ^{ 133 | helperCenterView.center = keyWindow.center; 134 | } 135 | completion: ^(BOOL finished) { 136 | if (finished) { 137 | UITapGestureRecognizer *tapGes = [[UITapGestureRecognizer alloc] initWithTarget:self action: @selector(tapToUntrigger)]; 138 | [blurView addGestureRecognizer: tapGes]; 139 | [self finishAnimation]; 140 | } 141 | }]; 142 | [self animateButtons]; 143 | triggered = YES; 144 | } else { 145 | [self tapToUntrigger]; 146 | } 147 | } 148 | 149 | - (void) animateButtons{ 150 | for (NSInteger i = 0; i < self.subviews.count; i++) { 151 | 152 | UIView *menuButton = self.subviews[i]; 153 | menuButton.transform = CGAffineTransformMakeTranslation(0, -90); 154 | [UIView animateWithDuration: 0.7 155 | delay: i * (0.3 / self.subviews.count) 156 | usingSpringWithDamping: 0.6f 157 | initialSpringVelocity: 0.0f 158 | options: UIViewAnimationOptionBeginFromCurrentState | UIViewAnimationOptionAllowUserInteraction 159 | animations: ^{ 160 | menuButton.transform = CGAffineTransformIdentity; 161 | } 162 | completion: NULL]; 163 | } 164 | 165 | } 166 | 167 | - (void) tapToUntrigger{ 168 | 169 | [UIView animateWithDuration: 0.618 animations:^{ 170 | self.frame = CGRectMake(0, khei + khei / 2 + menuBlankWidth, kwid, khei / 2 + menuBlankWidth); 171 | }]; 172 | 173 | [self beforeAnimation]; 174 | [UIView animateWithDuration: 1 175 | delay: 0.0f 176 | usingSpringWithDamping: 0.6f 177 | initialSpringVelocity: 0.9f 178 | options: UIViewAnimationOptionBeginFromCurrentState | UIViewAnimationOptionAllowUserInteraction 179 | animations: ^{ 180 | helperSideView.center = CGPointMake(20, hei + 20); 181 | } 182 | completion: ^(BOOL finished) { 183 | [self finishAnimation]; 184 | }]; 185 | 186 | [UIView animateWithDuration:0.3 animations: ^{ 187 | blurView.alpha = 0.0f; 188 | }]; 189 | 190 | [self beforeAnimation]; 191 | [UIView animateWithDuration: 1 192 | delay: 0.0f 193 | usingSpringWithDamping: 0.7f 194 | initialSpringVelocity: 2.0f 195 | options: UIViewAnimationOptionBeginFromCurrentState | UIViewAnimationOptionAllowUserInteraction 196 | animations: ^{ 197 | helperCenterView.center = CGPointMake(wid / 2, hei + 20); 198 | } 199 | completion: ^(BOOL finished) { 200 | [self finishAnimation]; 201 | }]; 202 | 203 | triggered = NO; 204 | 205 | } 206 | 207 | //动画之前调用 208 | - (void) beforeAnimation{ 209 | if (self.displayLink == nil) { 210 | self.displayLink = [CADisplayLink displayLinkWithTarget: self selector: @selector(displayLinkAction:)]; 211 | [self.displayLink addToRunLoop: [NSRunLoop mainRunLoop] forMode: NSDefaultRunLoopMode]; 212 | } 213 | self.animationCount ++; 214 | } 215 | 216 | //动画完成之后调用 217 | - (void) finishAnimation{ 218 | self.animationCount --; 219 | if (self.animationCount == 0) { 220 | [self.displayLink invalidate]; 221 | self.displayLink = nil; 222 | } 223 | } 224 | 225 | - (void) displayLinkAction: (CADisplayLink *)dis{ 226 | 227 | CALayer *sideHelperPresentationLayer = (CALayer *)[helperSideView.layer presentationLayer]; 228 | CALayer *centerHelperPresentationLayer = (CALayer *)[helperCenterView.layer presentationLayer]; 229 | 230 | CGRect centerRect = [[centerHelperPresentationLayer valueForKeyPath:@"frame"] CGRectValue]; 231 | CGRect sideRect = [[sideHelperPresentationLayer valueForKeyPath:@"frame"] CGRectValue]; 232 | 233 | diff = sideRect.origin.y - centerRect.origin.y; 234 | 235 | 236 | // 重新布局方法 237 | // 在receiver标上一个需要被重新绘图的标记,在下一个draw周期自动重绘 238 | // 默认runloop周期 60Hz 239 | [self setNeedsDisplay]; 240 | } 241 | 242 | @end 243 | -------------------------------------------------------------------------------- /DGGooeySlide ObjC/DGGooeySlide.xcodeproj/project.pbxproj: -------------------------------------------------------------------------------- 1 | // !$*UTF8*$! 2 | { 3 | archiveVersion = 1; 4 | classes = { 5 | }; 6 | objectVersion = 46; 7 | objects = { 8 | 9 | /* Begin PBXBuildFile section */ 10 | D53F27D91D0252E900A0704C /* main.m in Sources */ = {isa = PBXBuildFile; fileRef = D53F27D81D0252E900A0704C /* main.m */; }; 11 | D53F27DC1D0252E900A0704C /* AppDelegate.m in Sources */ = {isa = PBXBuildFile; fileRef = D53F27DB1D0252E900A0704C /* AppDelegate.m */; }; 12 | D53F27DF1D0252E900A0704C /* ViewController.m in Sources */ = {isa = PBXBuildFile; fileRef = D53F27DE1D0252E900A0704C /* ViewController.m */; }; 13 | D53F27E21D0252E900A0704C /* Main.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = D53F27E01D0252E900A0704C /* Main.storyboard */; }; 14 | D53F27E41D0252E900A0704C /* Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = D53F27E31D0252E900A0704C /* Assets.xcassets */; }; 15 | D53F27E71D0252E900A0704C /* LaunchScreen.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = D53F27E51D0252E900A0704C /* LaunchScreen.storyboard */; }; 16 | D53F27F11D02548A00A0704C /* DGGooeySlideMenu.m in Sources */ = {isa = PBXBuildFile; fileRef = D53F27F01D02548A00A0704C /* DGGooeySlideMenu.m */; }; 17 | /* End PBXBuildFile section */ 18 | 19 | /* Begin PBXFileReference section */ 20 | D53F27D41D0252E900A0704C /* DGGooeySlide.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = DGGooeySlide.app; sourceTree = BUILT_PRODUCTS_DIR; }; 21 | D53F27D81D0252E900A0704C /* main.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = main.m; sourceTree = ""; }; 22 | D53F27DA1D0252E900A0704C /* AppDelegate.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = AppDelegate.h; sourceTree = ""; }; 23 | D53F27DB1D0252E900A0704C /* AppDelegate.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = AppDelegate.m; sourceTree = ""; }; 24 | D53F27DD1D0252E900A0704C /* ViewController.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = ViewController.h; sourceTree = ""; }; 25 | D53F27DE1D0252E900A0704C /* ViewController.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = ViewController.m; sourceTree = ""; }; 26 | D53F27E11D0252E900A0704C /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; name = Base; path = Base.lproj/Main.storyboard; sourceTree = ""; }; 27 | D53F27E31D0252E900A0704C /* Assets.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = Assets.xcassets; sourceTree = ""; }; 28 | D53F27E61D0252E900A0704C /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; name = Base; path = Base.lproj/LaunchScreen.storyboard; sourceTree = ""; }; 29 | D53F27E81D0252E900A0704C /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; }; 30 | D53F27EF1D02548A00A0704C /* DGGooeySlideMenu.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = DGGooeySlideMenu.h; sourceTree = ""; }; 31 | D53F27F01D02548A00A0704C /* DGGooeySlideMenu.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = DGGooeySlideMenu.m; sourceTree = ""; }; 32 | /* End PBXFileReference section */ 33 | 34 | /* Begin PBXFrameworksBuildPhase section */ 35 | D53F27D11D0252E900A0704C /* Frameworks */ = { 36 | isa = PBXFrameworksBuildPhase; 37 | buildActionMask = 2147483647; 38 | files = ( 39 | ); 40 | runOnlyForDeploymentPostprocessing = 0; 41 | }; 42 | /* End PBXFrameworksBuildPhase section */ 43 | 44 | /* Begin PBXGroup section */ 45 | D53F27CB1D0252E900A0704C = { 46 | isa = PBXGroup; 47 | children = ( 48 | D53F27D61D0252E900A0704C /* DGGooeySlide */, 49 | D53F27D51D0252E900A0704C /* Products */, 50 | ); 51 | sourceTree = ""; 52 | }; 53 | D53F27D51D0252E900A0704C /* Products */ = { 54 | isa = PBXGroup; 55 | children = ( 56 | D53F27D41D0252E900A0704C /* DGGooeySlide.app */, 57 | ); 58 | name = Products; 59 | sourceTree = ""; 60 | }; 61 | D53F27D61D0252E900A0704C /* DGGooeySlide */ = { 62 | isa = PBXGroup; 63 | children = ( 64 | D53F27EE1D02539200A0704C /* Classes */, 65 | D53F27DA1D0252E900A0704C /* AppDelegate.h */, 66 | D53F27DB1D0252E900A0704C /* AppDelegate.m */, 67 | D53F27DD1D0252E900A0704C /* ViewController.h */, 68 | D53F27DE1D0252E900A0704C /* ViewController.m */, 69 | D53F27E01D0252E900A0704C /* Main.storyboard */, 70 | D53F27E31D0252E900A0704C /* Assets.xcassets */, 71 | D53F27E51D0252E900A0704C /* LaunchScreen.storyboard */, 72 | D53F27E81D0252E900A0704C /* Info.plist */, 73 | D53F27D71D0252E900A0704C /* Supporting Files */, 74 | ); 75 | path = DGGooeySlide; 76 | sourceTree = ""; 77 | }; 78 | D53F27D71D0252E900A0704C /* Supporting Files */ = { 79 | isa = PBXGroup; 80 | children = ( 81 | D53F27D81D0252E900A0704C /* main.m */, 82 | ); 83 | name = "Supporting Files"; 84 | sourceTree = ""; 85 | }; 86 | D53F27EE1D02539200A0704C /* Classes */ = { 87 | isa = PBXGroup; 88 | children = ( 89 | D53F27EF1D02548A00A0704C /* DGGooeySlideMenu.h */, 90 | D53F27F01D02548A00A0704C /* DGGooeySlideMenu.m */, 91 | ); 92 | name = Classes; 93 | sourceTree = ""; 94 | }; 95 | /* End PBXGroup section */ 96 | 97 | /* Begin PBXNativeTarget section */ 98 | D53F27D31D0252E900A0704C /* DGGooeySlide */ = { 99 | isa = PBXNativeTarget; 100 | buildConfigurationList = D53F27EB1D0252E900A0704C /* Build configuration list for PBXNativeTarget "DGGooeySlide" */; 101 | buildPhases = ( 102 | D53F27D01D0252E900A0704C /* Sources */, 103 | D53F27D11D0252E900A0704C /* Frameworks */, 104 | D53F27D21D0252E900A0704C /* Resources */, 105 | ); 106 | buildRules = ( 107 | ); 108 | dependencies = ( 109 | ); 110 | name = DGGooeySlide; 111 | productName = DGGooeySlide; 112 | productReference = D53F27D41D0252E900A0704C /* DGGooeySlide.app */; 113 | productType = "com.apple.product-type.application"; 114 | }; 115 | /* End PBXNativeTarget section */ 116 | 117 | /* Begin PBXProject section */ 118 | D53F27CC1D0252E900A0704C /* Project object */ = { 119 | isa = PBXProject; 120 | attributes = { 121 | CLASSPREFIX = DG; 122 | LastUpgradeCheck = 0730; 123 | ORGANIZATIONNAME = Desgard_Duan; 124 | TargetAttributes = { 125 | D53F27D31D0252E900A0704C = { 126 | CreatedOnToolsVersion = 7.3; 127 | DevelopmentTeam = C3JGTFM664; 128 | }; 129 | }; 130 | }; 131 | buildConfigurationList = D53F27CF1D0252E900A0704C /* Build configuration list for PBXProject "DGGooeySlide" */; 132 | compatibilityVersion = "Xcode 3.2"; 133 | developmentRegion = English; 134 | hasScannedForEncodings = 0; 135 | knownRegions = ( 136 | en, 137 | Base, 138 | ); 139 | mainGroup = D53F27CB1D0252E900A0704C; 140 | productRefGroup = D53F27D51D0252E900A0704C /* Products */; 141 | projectDirPath = ""; 142 | projectRoot = ""; 143 | targets = ( 144 | D53F27D31D0252E900A0704C /* DGGooeySlide */, 145 | ); 146 | }; 147 | /* End PBXProject section */ 148 | 149 | /* Begin PBXResourcesBuildPhase section */ 150 | D53F27D21D0252E900A0704C /* Resources */ = { 151 | isa = PBXResourcesBuildPhase; 152 | buildActionMask = 2147483647; 153 | files = ( 154 | D53F27E71D0252E900A0704C /* LaunchScreen.storyboard in Resources */, 155 | D53F27E41D0252E900A0704C /* Assets.xcassets in Resources */, 156 | D53F27E21D0252E900A0704C /* Main.storyboard in Resources */, 157 | ); 158 | runOnlyForDeploymentPostprocessing = 0; 159 | }; 160 | /* End PBXResourcesBuildPhase section */ 161 | 162 | /* Begin PBXSourcesBuildPhase section */ 163 | D53F27D01D0252E900A0704C /* Sources */ = { 164 | isa = PBXSourcesBuildPhase; 165 | buildActionMask = 2147483647; 166 | files = ( 167 | D53F27DF1D0252E900A0704C /* ViewController.m in Sources */, 168 | D53F27F11D02548A00A0704C /* DGGooeySlideMenu.m in Sources */, 169 | D53F27DC1D0252E900A0704C /* AppDelegate.m in Sources */, 170 | D53F27D91D0252E900A0704C /* main.m in Sources */, 171 | ); 172 | runOnlyForDeploymentPostprocessing = 0; 173 | }; 174 | /* End PBXSourcesBuildPhase section */ 175 | 176 | /* Begin PBXVariantGroup section */ 177 | D53F27E01D0252E900A0704C /* Main.storyboard */ = { 178 | isa = PBXVariantGroup; 179 | children = ( 180 | D53F27E11D0252E900A0704C /* Base */, 181 | ); 182 | name = Main.storyboard; 183 | sourceTree = ""; 184 | }; 185 | D53F27E51D0252E900A0704C /* LaunchScreen.storyboard */ = { 186 | isa = PBXVariantGroup; 187 | children = ( 188 | D53F27E61D0252E900A0704C /* Base */, 189 | ); 190 | name = LaunchScreen.storyboard; 191 | sourceTree = ""; 192 | }; 193 | /* End PBXVariantGroup section */ 194 | 195 | /* Begin XCBuildConfiguration section */ 196 | D53F27E91D0252E900A0704C /* Debug */ = { 197 | isa = XCBuildConfiguration; 198 | buildSettings = { 199 | ALWAYS_SEARCH_USER_PATHS = NO; 200 | CLANG_ANALYZER_NONNULL = YES; 201 | CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x"; 202 | CLANG_CXX_LIBRARY = "libc++"; 203 | CLANG_ENABLE_MODULES = YES; 204 | CLANG_ENABLE_OBJC_ARC = YES; 205 | CLANG_WARN_BOOL_CONVERSION = YES; 206 | CLANG_WARN_CONSTANT_CONVERSION = YES; 207 | CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; 208 | CLANG_WARN_EMPTY_BODY = YES; 209 | CLANG_WARN_ENUM_CONVERSION = YES; 210 | CLANG_WARN_INT_CONVERSION = YES; 211 | CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; 212 | CLANG_WARN_UNREACHABLE_CODE = YES; 213 | CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; 214 | "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer"; 215 | COPY_PHASE_STRIP = NO; 216 | DEBUG_INFORMATION_FORMAT = dwarf; 217 | ENABLE_STRICT_OBJC_MSGSEND = YES; 218 | ENABLE_TESTABILITY = YES; 219 | GCC_C_LANGUAGE_STANDARD = gnu99; 220 | GCC_DYNAMIC_NO_PIC = NO; 221 | GCC_NO_COMMON_BLOCKS = YES; 222 | GCC_OPTIMIZATION_LEVEL = 0; 223 | GCC_PREPROCESSOR_DEFINITIONS = ( 224 | "DEBUG=1", 225 | "$(inherited)", 226 | ); 227 | GCC_WARN_64_TO_32_BIT_CONVERSION = YES; 228 | GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; 229 | GCC_WARN_UNDECLARED_SELECTOR = YES; 230 | GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; 231 | GCC_WARN_UNUSED_FUNCTION = YES; 232 | GCC_WARN_UNUSED_VARIABLE = YES; 233 | IPHONEOS_DEPLOYMENT_TARGET = 9.3; 234 | MTL_ENABLE_DEBUG_INFO = YES; 235 | ONLY_ACTIVE_ARCH = YES; 236 | SDKROOT = iphoneos; 237 | }; 238 | name = Debug; 239 | }; 240 | D53F27EA1D0252E900A0704C /* Release */ = { 241 | isa = XCBuildConfiguration; 242 | buildSettings = { 243 | ALWAYS_SEARCH_USER_PATHS = NO; 244 | CLANG_ANALYZER_NONNULL = YES; 245 | CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x"; 246 | CLANG_CXX_LIBRARY = "libc++"; 247 | CLANG_ENABLE_MODULES = YES; 248 | CLANG_ENABLE_OBJC_ARC = YES; 249 | CLANG_WARN_BOOL_CONVERSION = YES; 250 | CLANG_WARN_CONSTANT_CONVERSION = YES; 251 | CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; 252 | CLANG_WARN_EMPTY_BODY = YES; 253 | CLANG_WARN_ENUM_CONVERSION = YES; 254 | CLANG_WARN_INT_CONVERSION = YES; 255 | CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; 256 | CLANG_WARN_UNREACHABLE_CODE = YES; 257 | CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; 258 | "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer"; 259 | COPY_PHASE_STRIP = NO; 260 | DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym"; 261 | ENABLE_NS_ASSERTIONS = NO; 262 | ENABLE_STRICT_OBJC_MSGSEND = YES; 263 | GCC_C_LANGUAGE_STANDARD = gnu99; 264 | GCC_NO_COMMON_BLOCKS = YES; 265 | GCC_WARN_64_TO_32_BIT_CONVERSION = YES; 266 | GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; 267 | GCC_WARN_UNDECLARED_SELECTOR = YES; 268 | GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; 269 | GCC_WARN_UNUSED_FUNCTION = YES; 270 | GCC_WARN_UNUSED_VARIABLE = YES; 271 | IPHONEOS_DEPLOYMENT_TARGET = 9.3; 272 | MTL_ENABLE_DEBUG_INFO = NO; 273 | SDKROOT = iphoneos; 274 | VALIDATE_PRODUCT = YES; 275 | }; 276 | name = Release; 277 | }; 278 | D53F27EC1D0252E900A0704C /* Debug */ = { 279 | isa = XCBuildConfiguration; 280 | buildSettings = { 281 | ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; 282 | CODE_SIGN_IDENTITY = "iPhone Developer"; 283 | INFOPLIST_FILE = DGGooeySlide/Info.plist; 284 | LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks"; 285 | PRODUCT_BUNDLE_IDENTIFIER = desgard.express.DGGooeySlide; 286 | PRODUCT_NAME = "$(TARGET_NAME)"; 287 | }; 288 | name = Debug; 289 | }; 290 | D53F27ED1D0252E900A0704C /* Release */ = { 291 | isa = XCBuildConfiguration; 292 | buildSettings = { 293 | ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; 294 | CODE_SIGN_IDENTITY = "iPhone Developer"; 295 | INFOPLIST_FILE = DGGooeySlide/Info.plist; 296 | LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks"; 297 | PRODUCT_BUNDLE_IDENTIFIER = desgard.express.DGGooeySlide; 298 | PRODUCT_NAME = "$(TARGET_NAME)"; 299 | }; 300 | name = Release; 301 | }; 302 | /* End XCBuildConfiguration section */ 303 | 304 | /* Begin XCConfigurationList section */ 305 | D53F27CF1D0252E900A0704C /* Build configuration list for PBXProject "DGGooeySlide" */ = { 306 | isa = XCConfigurationList; 307 | buildConfigurations = ( 308 | D53F27E91D0252E900A0704C /* Debug */, 309 | D53F27EA1D0252E900A0704C /* Release */, 310 | ); 311 | defaultConfigurationIsVisible = 0; 312 | defaultConfigurationName = Release; 313 | }; 314 | D53F27EB1D0252E900A0704C /* Build configuration list for PBXNativeTarget "DGGooeySlide" */ = { 315 | isa = XCConfigurationList; 316 | buildConfigurations = ( 317 | D53F27EC1D0252E900A0704C /* Debug */, 318 | D53F27ED1D0252E900A0704C /* Release */, 319 | ); 320 | defaultConfigurationIsVisible = 0; 321 | }; 322 | /* End XCConfigurationList section */ 323 | }; 324 | rootObject = D53F27CC1D0252E900A0704C /* Project object */; 325 | } 326 | --------------------------------------------------------------------------------