├── CircleSlider1.gif ├── CircleSlider2.gif ├── ZCircleSliderDemo ├── ZCircleSliderDemo.xcodeproj │ ├── project.xcworkspace │ │ ├── contents.xcworkspacedata │ │ └── xcuserdata │ │ │ └── zjibo.xcuserdatad │ │ │ └── UserInterfaceState.xcuserstate │ ├── xcuserdata │ │ ├── zjixin.xcuserdatad │ │ │ └── xcschemes │ │ │ │ └── xcschememanagement.plist │ │ ├── wscn.xcuserdatad │ │ │ └── xcschemes │ │ │ │ ├── xcschememanagement.plist │ │ │ │ └── ZCircleSliderDemo.xcscheme │ │ └── zjibo.xcuserdatad │ │ │ ├── xcschemes │ │ │ ├── xcschememanagement.plist │ │ │ └── ZCircleSliderDemo.xcscheme │ │ │ └── xcdebugger │ │ │ └── Breakpoints_v2.xcbkptlist │ └── project.pbxproj └── ZCircleSliderDemo │ ├── ViewController.h │ ├── FirstViewController.h │ ├── SecondViewController.h │ ├── MainTableViewController.h │ ├── AppDelegate.h │ ├── main.m │ ├── ViewController.m │ ├── DefaultDefine.h │ ├── Assets.xcassets │ └── AppIcon.appiconset │ │ └── Contents.json │ ├── ZCircleSlider │ ├── ZCircleSlider.h │ └── ZCircleSlider.m │ ├── Info.plist │ ├── Base.lproj │ ├── LaunchScreen.storyboard │ └── Main.storyboard │ ├── MainTableViewController.m │ ├── AppDelegate.m │ ├── FirstViewController.m │ └── SecondViewController.m ├── ZCircleSlider ├── ZCircleSlider.h └── ZCircleSlider.m └── README.md /CircleSlider1.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/JixinZhang/ZCircleSlider/HEAD/CircleSlider1.gif -------------------------------------------------------------------------------- /CircleSlider2.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/JixinZhang/ZCircleSlider/HEAD/CircleSlider2.gif -------------------------------------------------------------------------------- /ZCircleSliderDemo/ZCircleSliderDemo.xcodeproj/project.xcworkspace/contents.xcworkspacedata: -------------------------------------------------------------------------------- 1 | 2 | 4 | 6 | 7 | 8 | -------------------------------------------------------------------------------- /ZCircleSliderDemo/ZCircleSliderDemo.xcodeproj/project.xcworkspace/xcuserdata/zjibo.xcuserdatad/UserInterfaceState.xcuserstate: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/JixinZhang/ZCircleSlider/HEAD/ZCircleSliderDemo/ZCircleSliderDemo.xcodeproj/project.xcworkspace/xcuserdata/zjibo.xcuserdatad/UserInterfaceState.xcuserstate -------------------------------------------------------------------------------- /ZCircleSliderDemo/ZCircleSliderDemo/ViewController.h: -------------------------------------------------------------------------------- 1 | // 2 | // ViewController.h 3 | // ZCircleSliderDemo 4 | // 5 | // Created by ZhangBob on 01/06/2017. 6 | // Copyright © 2017 Jixin. All rights reserved. 7 | // 8 | 9 | #import 10 | 11 | @interface ViewController : UIViewController 12 | 13 | 14 | @end 15 | 16 | -------------------------------------------------------------------------------- /ZCircleSliderDemo/ZCircleSliderDemo/FirstViewController.h: -------------------------------------------------------------------------------- 1 | // 2 | // FirstViewController.h 3 | // ZCircleSliderDemo 4 | // 5 | // Created by ZhangBob on 01/06/2017. 6 | // Copyright © 2017 Jixin. All rights reserved. 7 | // 8 | 9 | #import 10 | 11 | @interface FirstViewController : UIViewController 12 | 13 | @end 14 | -------------------------------------------------------------------------------- /ZCircleSliderDemo/ZCircleSliderDemo/SecondViewController.h: -------------------------------------------------------------------------------- 1 | // 2 | // SecondViewController.h 3 | // ZCircleSliderDemo 4 | // 5 | // Created by ZhangBob on 01/06/2017. 6 | // Copyright © 2017 Jixin. All rights reserved. 7 | // 8 | 9 | #import 10 | 11 | @interface SecondViewController : UIViewController 12 | 13 | @end 14 | -------------------------------------------------------------------------------- /ZCircleSliderDemo/ZCircleSliderDemo/MainTableViewController.h: -------------------------------------------------------------------------------- 1 | // 2 | // MainTableViewController.h 3 | // ZCircleSliderDemo 4 | // 5 | // Created by ZhangBob on 01/06/2017. 6 | // Copyright © 2017 Jixin. All rights reserved. 7 | // 8 | 9 | #import 10 | 11 | @interface MainTableViewController : UITableViewController 12 | 13 | @end 14 | -------------------------------------------------------------------------------- /ZCircleSliderDemo/ZCircleSliderDemo/AppDelegate.h: -------------------------------------------------------------------------------- 1 | // 2 | // AppDelegate.h 3 | // ZCircleSliderDemo 4 | // 5 | // Created by ZhangBob on 01/06/2017. 6 | // Copyright © 2017 Jixin. 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 | -------------------------------------------------------------------------------- /ZCircleSliderDemo/ZCircleSliderDemo/main.m: -------------------------------------------------------------------------------- 1 | // 2 | // main.m 3 | // ZCircleSliderDemo 4 | // 5 | // Created by ZhangBob on 01/06/2017. 6 | // Copyright © 2017 Jixin. 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 | -------------------------------------------------------------------------------- /ZCircleSliderDemo/ZCircleSliderDemo.xcodeproj/xcuserdata/zjixin.xcuserdatad/xcschemes/xcschememanagement.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | SchemeUserState 6 | 7 | ZCircleSliderDemo.xcscheme_^#shared#^_ 8 | 9 | orderHint 10 | 3 11 | 12 | 13 | 14 | 15 | -------------------------------------------------------------------------------- /ZCircleSliderDemo/ZCircleSliderDemo/ViewController.m: -------------------------------------------------------------------------------- 1 | // 2 | // ViewController.m 3 | // ZCircleSliderDemo 4 | // 5 | // Created by ZhangBob on 01/06/2017. 6 | // Copyright © 2017 Jixin. All rights reserved. 7 | // 8 | 9 | #import "ViewController.h" 10 | 11 | @interface ViewController () 12 | 13 | @end 14 | 15 | @implementation ViewController 16 | 17 | - (void)viewDidLoad { 18 | [super viewDidLoad]; 19 | // Do any additional setup after loading the view, typically from a nib. 20 | } 21 | 22 | 23 | - (void)didReceiveMemoryWarning { 24 | [super didReceiveMemoryWarning]; 25 | // Dispose of any resources that can be recreated. 26 | } 27 | 28 | 29 | @end 30 | -------------------------------------------------------------------------------- /ZCircleSliderDemo/ZCircleSliderDemo.xcodeproj/xcuserdata/wscn.xcuserdatad/xcschemes/xcschememanagement.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | SchemeUserState 6 | 7 | ZCircleSliderDemo.xcscheme 8 | 9 | orderHint 10 | 0 11 | 12 | 13 | SuppressBuildableAutocreation 14 | 15 | 78F86A4D1EE0225C0016F81E 16 | 17 | primary 18 | 19 | 20 | 21 | 22 | 23 | -------------------------------------------------------------------------------- /ZCircleSliderDemo/ZCircleSliderDemo.xcodeproj/xcuserdata/zjibo.xcuserdatad/xcschemes/xcschememanagement.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | SchemeUserState 6 | 7 | ZCircleSliderDemo.xcscheme 8 | 9 | orderHint 10 | 0 11 | 12 | 13 | SuppressBuildableAutocreation 14 | 15 | 78F86A4D1EE0225C0016F81E 16 | 17 | primary 18 | 19 | 20 | 21 | 22 | 23 | -------------------------------------------------------------------------------- /ZCircleSliderDemo/ZCircleSliderDemo/DefaultDefine.h: -------------------------------------------------------------------------------- 1 | // 2 | // DefaultDefine.h 3 | // ZCircleSliderDemo 4 | // 5 | // Created by ZhangBob on 01/06/2017. 6 | // Copyright © 2017 Jixin. All rights reserved. 7 | // 8 | 9 | #ifndef DefaultDefine_h 10 | #define DefaultDefine_h 11 | 12 | #define kScreenWidth [UIScreen mainScreen].bounds.size.width 13 | #define kScreenHeight [UIScreen mainScreen].bounds.size.height 14 | 15 | //十六进制色值 16 | #define kUIColorFromRGB(rgbValue) [UIColor \ 17 | colorWithRed:((float)((rgbValue & 0xFF0000) >> 16))/255.0 \ 18 | green:((float)((rgbValue & 0xFF00) >> 8))/255.0 \ 19 | blue:((float)(rgbValue & 0xFF))/255.0 alpha:1.0] 20 | 21 | #endif /* DefaultDefine_h */ 22 | -------------------------------------------------------------------------------- /ZCircleSliderDemo/ZCircleSliderDemo.xcodeproj/xcuserdata/zjibo.xcuserdatad/xcdebugger/Breakpoints_v2.xcbkptlist: -------------------------------------------------------------------------------- 1 | 2 | 5 | 6 | 8 | 20 | 21 | 22 | 23 | 24 | -------------------------------------------------------------------------------- /ZCircleSliderDemo/ZCircleSliderDemo/Assets.xcassets/AppIcon.appiconset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "idiom" : "iphone", 5 | "size" : "20x20", 6 | "scale" : "2x" 7 | }, 8 | { 9 | "idiom" : "iphone", 10 | "size" : "20x20", 11 | "scale" : "3x" 12 | }, 13 | { 14 | "idiom" : "iphone", 15 | "size" : "29x29", 16 | "scale" : "2x" 17 | }, 18 | { 19 | "idiom" : "iphone", 20 | "size" : "29x29", 21 | "scale" : "3x" 22 | }, 23 | { 24 | "idiom" : "iphone", 25 | "size" : "40x40", 26 | "scale" : "2x" 27 | }, 28 | { 29 | "idiom" : "iphone", 30 | "size" : "40x40", 31 | "scale" : "3x" 32 | }, 33 | { 34 | "idiom" : "iphone", 35 | "size" : "60x60", 36 | "scale" : "2x" 37 | }, 38 | { 39 | "idiom" : "iphone", 40 | "size" : "60x60", 41 | "scale" : "3x" 42 | } 43 | ], 44 | "info" : { 45 | "version" : 1, 46 | "author" : "xcode" 47 | } 48 | } -------------------------------------------------------------------------------- /ZCircleSlider/ZCircleSlider.h: -------------------------------------------------------------------------------- 1 | // 2 | // ZCircleSlider.h 3 | // LoadingView 4 | // 5 | // Created by ZhangBob on 24/05/2017. 6 | // Copyright © 2017 JixinZhang. All rights reserved. 7 | // 8 | 9 | #import 10 | 11 | @interface ZCircleSlider : UIControl 12 | 13 | @property (nullable, nonatomic, strong) UIColor *backgroundTintColor; 14 | @property (nullable, nonatomic, strong) UIColor *minimumTrackTintColor; 15 | @property (nullable, nonatomic, strong) UIColor *maximumTrackTintColor; 16 | @property (nullable, nonatomic, strong) UIColor *thumbTintColor; 17 | 18 | @property (nonatomic, assign) CGFloat circleBorderWidth; //圆的宽度 19 | @property (nonatomic, assign) CGFloat circleRadius; //圆形进度条的半径,一般比view的宽高中最小者还要小24 20 | @property (nonatomic, assign) CGFloat thumbRadius; //滑块正常的半径 21 | @property (nonatomic, assign) CGFloat thumbExpandRadius; //滑块放大的半径 22 | 23 | @property (nonatomic, assign) float value; //slider当前的value 24 | @property (nonatomic, assign) float loadProgress; //slider加载的进度 25 | 26 | @property (nonatomic, assign) BOOL canRepeat; //是否可以重复拖动。默认为NO,即只能转到360;否则任意角度。 27 | @property (nonatomic, assign, readonly) BOOL interaction; //点击在限定的区域为YES,否则为NO. 28 | 29 | @end 30 | -------------------------------------------------------------------------------- /ZCircleSliderDemo/ZCircleSliderDemo/ZCircleSlider/ZCircleSlider.h: -------------------------------------------------------------------------------- 1 | // 2 | // ZCircleSlider.h 3 | // LoadingView 4 | // 5 | // Created by ZhangBob on 24/05/2017. 6 | // Copyright © 2017 JixinZhang. All rights reserved. 7 | // 8 | 9 | #import 10 | 11 | /** 12 | * 圆环形滑块,和UISlider类似,既可以显示进度又可以改变进度 13 | */ 14 | @interface ZCircleSlider : UIControl 15 | 16 | @property (nullable, nonatomic, strong) UIColor *backgroundTintColor; //圆环的背景色 17 | @property (nullable, nonatomic, strong) UIColor *minimumTrackTintColor; //圆环滑过的颜色 18 | @property (nullable, nonatomic, strong) UIColor *maximumTrackTintColor; //圆环加载进度的颜色,加载完成后就相当于圆环未滑过的颜色 19 | @property (nullable, nonatomic, strong) UIColor *thumbTintColor; //滑块的颜色 20 | 21 | @property (nonatomic, assign) CGFloat circleBorderWidth; //圆的宽度 22 | @property (nonatomic, assign) CGFloat circleRadius; //圆形进度条的半径,一般比view的宽高中最小者还要小24 23 | @property (nonatomic, assign) CGFloat thumbRadius; //滑块正常的半径 24 | @property (nonatomic, assign) CGFloat thumbExpandRadius; //滑块放大的半径 25 | 26 | @property (nonatomic, assign, readonly) BOOL interaction; //点击在限定的区域为YES,否则为NO. 27 | @property (nonatomic, assign) BOOL canRepeat; //是否可以重复拖动。默认为NO,即只能转到360;否则任意角度。 28 | 29 | @property (nonatomic, assign) float value; //slider当前的value 30 | @property (nonatomic, assign) float loadProgress; //slider加载的进度 31 | 32 | @end 33 | -------------------------------------------------------------------------------- /ZCircleSliderDemo/ZCircleSliderDemo/Info.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | CFBundleDevelopmentRegion 6 | en 7 | CFBundleDisplayName 8 | 圆形滑块 9 | CFBundleExecutable 10 | $(EXECUTABLE_NAME) 11 | CFBundleIdentifier 12 | $(PRODUCT_BUNDLE_IDENTIFIER) 13 | CFBundleInfoDictionaryVersion 14 | 6.0 15 | CFBundleName 16 | $(PRODUCT_NAME) 17 | CFBundlePackageType 18 | APPL 19 | CFBundleShortVersionString 20 | 1.0 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 | -------------------------------------------------------------------------------- /ZCircleSliderDemo/ZCircleSliderDemo/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 | -------------------------------------------------------------------------------- /ZCircleSliderDemo/ZCircleSliderDemo/MainTableViewController.m: -------------------------------------------------------------------------------- 1 | // 2 | // MainTableViewController.m 3 | // ZCircleSliderDemo 4 | // 5 | // Created by ZhangBob on 01/06/2017. 6 | // Copyright © 2017 Jixin. All rights reserved. 7 | // 8 | 9 | #import "MainTableViewController.h" 10 | 11 | @interface MainTableViewController () 12 | @property (nonatomic, strong) NSArray *datas; 13 | @end 14 | 15 | @implementation MainTableViewController 16 | 17 | - (void)viewDidLoad { 18 | [super viewDidLoad]; 19 | self.title = @"Circle Slider"; 20 | 21 | self.datas = @[@{@"name":@"圆环形Slider,重复拖动", @"class":@"FirstViewController"}, 22 | @{@"name":@"圆环形Slider,限定360度", @"class":@"SecondViewController"}]; 23 | [self.tableView registerClass:[UITableViewCell class] forCellReuseIdentifier:NSStringFromClass([UITableViewCell class])]; 24 | } 25 | 26 | - (void)didReceiveMemoryWarning { 27 | [super didReceiveMemoryWarning]; 28 | // Dispose of any resources that can be recreated. 29 | } 30 | 31 | #pragma mark - Table view data source 32 | 33 | - (NSInteger)numberOfSectionsInTableView:(UITableView *)tableView { 34 | return 1; 35 | } 36 | 37 | - (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section { 38 | return self.datas.count; 39 | } 40 | 41 | - (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath { 42 | UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:NSStringFromClass([UITableViewCell class]) forIndexPath:indexPath]; 43 | cell.textLabel.text = _datas[indexPath.row][@"name"]; 44 | return cell; 45 | } 46 | 47 | - (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath { 48 | UIViewController *controller = [[NSClassFromString(_datas[indexPath.row][@"class"]) alloc] init]; 49 | controller.title = _datas[indexPath.row][@"name"]; 50 | [self.navigationController pushViewController:controller animated:YES]; 51 | } 52 | 53 | @end 54 | -------------------------------------------------------------------------------- /ZCircleSliderDemo/ZCircleSliderDemo/AppDelegate.m: -------------------------------------------------------------------------------- 1 | // 2 | // AppDelegate.m 3 | // ZCircleSliderDemo 4 | // 5 | // Created by ZhangBob on 01/06/2017. 6 | // Copyright © 2017 Jixin. 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 | 24 | - (void)applicationWillResignActive:(UIApplication *)application { 25 | // 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. 26 | // Use this method to pause ongoing tasks, disable timers, and invalidate graphics rendering callbacks. Games should use this method to pause the game. 27 | } 28 | 29 | 30 | - (void)applicationDidEnterBackground:(UIApplication *)application { 31 | // 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. 32 | // If your application supports background execution, this method is called instead of applicationWillTerminate: when the user quits. 33 | } 34 | 35 | 36 | - (void)applicationWillEnterForeground:(UIApplication *)application { 37 | // Called as part of the transition from the background to the active state; here you can undo many of the changes made on entering the background. 38 | } 39 | 40 | 41 | - (void)applicationDidBecomeActive:(UIApplication *)application { 42 | // 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. 43 | } 44 | 45 | 46 | - (void)applicationWillTerminate:(UIApplication *)application { 47 | // Called when the application is about to terminate. Save data if appropriate. See also applicationDidEnterBackground:. 48 | } 49 | 50 | 51 | @end 52 | -------------------------------------------------------------------------------- /ZCircleSliderDemo/ZCircleSliderDemo.xcodeproj/xcuserdata/wscn.xcuserdatad/xcschemes/ZCircleSliderDemo.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 | -------------------------------------------------------------------------------- /ZCircleSliderDemo/ZCircleSliderDemo.xcodeproj/xcuserdata/zjibo.xcuserdatad/xcschemes/ZCircleSliderDemo.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 | -------------------------------------------------------------------------------- /ZCircleSliderDemo/ZCircleSliderDemo/Base.lproj/Main.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 | 29 | 30 | 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 | -------------------------------------------------------------------------------- /ZCircleSliderDemo/ZCircleSliderDemo/FirstViewController.m: -------------------------------------------------------------------------------- 1 | // 2 | // FirstViewController.m 3 | // ZCircleSliderDemo 4 | // 5 | // Created by ZhangBob on 01/06/2017. 6 | // Copyright © 2017 Jixin. All rights reserved. 7 | // 8 | 9 | #import "FirstViewController.h" 10 | #import "ZCircleSlider.h" 11 | #import "DefaultDefine.h" 12 | 13 | @interface FirstViewController () 14 | 15 | @property (nonatomic, strong) ZCircleSlider *circleSlider; 16 | @property (nonatomic, strong) UILabel *currentValueLabel; 17 | @property (nonatomic, strong) UILabel *finalValueLabel; 18 | @property (nonatomic, strong) UISlider *progressSlider; 19 | 20 | @end 21 | 22 | @implementation FirstViewController 23 | 24 | - (void)viewDidLoad { 25 | [super viewDidLoad]; 26 | self.view.backgroundColor = [UIColor whiteColor]; 27 | [self.view addSubview:self.circleSlider]; 28 | [self.view addSubview:self.currentValueLabel]; 29 | [self.view addSubview:self.finalValueLabel]; 30 | [self.view addSubview:self.progressSlider]; 31 | } 32 | 33 | - (void)didReceiveMemoryWarning { 34 | [super didReceiveMemoryWarning]; 35 | // Dispose of any resources that can be recreated. 36 | } 37 | 38 | - (ZCircleSlider *)circleSlider { 39 | if (!_circleSlider) { 40 | _circleSlider = [[ZCircleSlider alloc] initWithFrame:CGRectMake((kScreenWidth - 300) / 2.0, (kScreenHeight - 300) / 2.0, 300, 300)]; 41 | _circleSlider.minimumTrackTintColor = kUIColorFromRGB(0x1482f0); 42 | _circleSlider.maximumTrackTintColor = kUIColorFromRGB(0xE6E9F5); 43 | _circleSlider.backgroundTintColor = [UIColor colorWithWhite:0 alpha:0.2]; 44 | _circleSlider.circleBorderWidth = 5.0f; 45 | _circleSlider.thumbRadius = 6; 46 | _circleSlider.thumbExpandRadius = 12.5; 47 | _circleSlider.thumbTintColor = [UIColor redColor]; 48 | _circleSlider.circleRadius = 290 / 2.0 + 2; 49 | _circleSlider.value = 0; 50 | _circleSlider.loadProgress = 0; 51 | _circleSlider.canRepeat = YES; 52 | [_circleSlider addTarget:self 53 | action:@selector(circleSliderTouchDown:) 54 | forControlEvents:UIControlEventTouchDown]; 55 | [_circleSlider addTarget:self 56 | action:@selector(circleSliderValueChanging:) 57 | forControlEvents:UIControlEventValueChanged]; 58 | [_circleSlider addTarget:self 59 | action:@selector(circleSliderValueDidChanged:) 60 | forControlEvents:UIControlEventTouchUpInside]; 61 | } 62 | return _circleSlider; 63 | } 64 | 65 | - (UILabel *)currentValueLabel { 66 | if (!_currentValueLabel) { 67 | _currentValueLabel = [[UILabel alloc] initWithFrame:CGRectMake((kScreenWidth - 100) / 2.0, (kScreenHeight - 30) / 2.0, 100, 30)]; 68 | _currentValueLabel.textAlignment = NSTextAlignmentCenter; 69 | _currentValueLabel.text = @"当前值:0"; 70 | } 71 | return _currentValueLabel; 72 | } 73 | 74 | - (UILabel *)finalValueLabel { 75 | if (!_finalValueLabel) { 76 | _finalValueLabel = [[UILabel alloc] initWithFrame:CGRectMake((kScreenWidth - 100) / 2.0, kScreenHeight - 90, 100, 30)]; 77 | _finalValueLabel.textAlignment = NSTextAlignmentCenter; 78 | _finalValueLabel.text = @"最终值:0"; 79 | } 80 | return _finalValueLabel; 81 | } 82 | 83 | - (UISlider *)progressSlider { 84 | if (!_progressSlider) { 85 | _progressSlider = [[UISlider alloc] initWithFrame:CGRectMake(53, kScreenHeight - 30, kScreenWidth - 53 * 2, 18)]; 86 | _progressSlider.backgroundColor = [UIColor clearColor]; 87 | _progressSlider.minimumValue = 0; 88 | _progressSlider.maximumValue = 1; 89 | _progressSlider.value = 0; 90 | [_progressSlider addTarget:self 91 | action:@selector(progressSliderValueChanging:) 92 | forControlEvents:UIControlEventValueChanged]; 93 | 94 | [_progressSlider addTarget:self 95 | action:@selector(progressSliderValueDidChanged:) 96 | forControlEvents:UIControlEventTouchUpInside]; 97 | 98 | } 99 | return _progressSlider; 100 | } 101 | 102 | #pragma mark - action 103 | 104 | - (IBAction)circleSliderTouchDown:(ZCircleSlider *)slider { 105 | if (!slider.interaction) { 106 | return; 107 | } 108 | 109 | } 110 | 111 | - (IBAction)circleSliderValueChanging:(ZCircleSlider *)slider { 112 | if (!slider.interaction) { 113 | return; 114 | } 115 | self.currentValueLabel.text = [NSString stringWithFormat:@"当前值:%.0f",slider.value * 100]; 116 | self.progressSlider.value = slider.value; 117 | } 118 | 119 | - (IBAction)circleSliderValueDidChanged:(ZCircleSlider *)slider { 120 | if (!slider.interaction) { 121 | return; 122 | } 123 | self.finalValueLabel.text = [NSString stringWithFormat:@"最终值:%.0f",slider.value * 100]; 124 | } 125 | 126 | - (IBAction)progressSliderValueChanging:(UISlider *)slider { 127 | self.currentValueLabel.text = [NSString stringWithFormat:@"当前值:%.0f",slider.value * 100]; 128 | self.circleSlider.value = slider.value; 129 | } 130 | 131 | - (IBAction)progressSliderValueDidChanged:(UISlider *)slider { 132 | self.progressSlider.value = slider.value; 133 | self.finalValueLabel.text = [NSString stringWithFormat:@"最终值:%.0f",slider.value * 100]; 134 | } 135 | 136 | @end 137 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # ZCircleSlider 2 | 3 | ## 一、圆环型滑块的设计 4 | * 最近设计师设计了一个圆环型滑块,其作用和UISlider差不多,用于拖动改变播放音频的进度和指示音频的播放进度。 5 | * 大概的样子如下图: 6 | ![图一 设计图](http://upload-images.jianshu.io/upload_images/2409226-4a19ac0071c373bc.png?imageMogr2/auto-orient/strip%7CimageView2/2/w/1240) 7 | * 有如下的特点: 8 | * 滑动的响应区域为圆环上,并且靠近滑块; 9 | * 点击滑块时,滑块有一个放大的动画,手指离开屏幕时滑块恢复原来大小; 10 | * 当value=0%时,滑块不可以再逆时针滑动,当value=100%时,滑块不可以再顺时针滑动。 11 | * 最后我自己再加了一个设计,指示音频loading的进度(这样以来圆环就有了三层)。 12 | 13 | 14 | ## 二、成果展示Demo 15 | * 重复拖动 16 | 17 | ![1.重复拖动](https://github.com/JixinZhang/ZCircleSlider/blob/master/CircleSlider1.gif) 18 | 19 | * 限定360度 20 | 21 | ![2.限定360度](https://github.com/JixinZhang/ZCircleSlider/blob/master/CircleSlider2.gif) 22 | 23 | ## 三、接入使用 24 | ###1. 接入 25 | 直接将[我的项目中](https://github.com/JixinZhang/ZCircleSlider)ZCircleSlider文件夹中的`ZCircleSlider.h 和 ZCircleSlider.m`拖到项目中即可 26 | 27 | ### 2.使用 28 | 29 | ``` 30 | //ZCircleSlider的背景色是透明的,不会挡住下面View 31 | - (ZCircleSlider *)circleSlider { 32 | if (!_circleSlider) { 33 | _circleSlider = [[ZCircleSlider alloc] initWithFrame:CGRectMake((kScreenWidth - 300) / 2.0, (kScreenHeight - 300) / 2.0, 300, 300)]; 34 | //走过的进度的颜色 35 | _circleSlider.minimumTrackTintColor = kUIColorFromRGB(0x1482f0); 36 | //loading进度的颜色。如果loading = 1,即loading完成,那么也是圆环的颜色,同于backgroundTintColor 37 | _circleSlider.maximumTrackTintColor = kUIColorFromRGB(0xE62E2E); 38 | //圆环的颜色 39 | _circleSlider.backgroundTintColor = [UIColor colorWithWhite:0 alpha:0.2]; 40 | //圆环的宽 41 | _circleSlider.circleBorderWidth = 5.0f; 42 | //圆形滑块的半径 43 | _circleSlider.thumbRadius = 8; 44 | //圆形滑块放大效果的半径 45 | _circleSlider.thumbExpandRadius = 12.5; 46 | //圆形滑块的颜色 47 | _circleSlider.thumbTintColor = [UIColor redColor]; 48 | //圆环的半径 49 | _circleSlider.circleRadius = 260 / 2.0 + 2; 50 | //设定初始值value = 0 51 | _circleSlider.value = 0; 52 | //设定loadingProgress的初始值 = 0 53 | _circleSlider.loadProgress = 0; 54 | //开始点击,响应事件 55 | [_circleSlider addTarget:self 56 | action:@selector(circleSliderTouchDown:) 57 | forControlEvents:UIControlEventTouchDown]; 58 | //拖动过程中,响应事件 59 | [_circleSlider addTarget:self 60 | action:@selector(circleSliderValueChanging:) 61 | forControlEvents:UIControlEventValueChanged]; 62 | //拖动结束,响应事件 63 | [_circleSlider addTarget:self 64 | action:@selector(circleSliderValueDidChanged:) 65 | forControlEvents:UIControlEventTouchUpInside]; 66 | } 67 | return _circleSlider; 68 | } 69 | 70 | #pragma mark - action 71 | 72 | /*以下三个方法,都要添加对slider.interaction的判断。 73 | *因为虽然看起来是个圆环,但是响应手势的区域确实整个矩形的View 74 | *在内部添加了interaction这个属性用于限定响应区域 75 | */ 76 | - (IBAction)circleSliderTouchDown:(ZCircleSlider *)slider { 77 | if (!slider.interaction) { 78 | return; 79 | } 80 | 81 | } 82 | 83 | - (IBAction)circleSliderValueChanging:(ZCircleSlider *)slider { 84 | if (!slider.interaction) { 85 | return; 86 | } 87 | self.currentValueLabel.text = [NSString stringWithFormat:@"当前值:%.0f",slider.value * 100]; 88 | self.progressSlider.value = slider.value; 89 | } 90 | 91 | - (IBAction)circleSliderValueDidChanged:(ZCircleSlider *)slider { 92 | if (!slider.interaction) { 93 | return; 94 | } 95 | self.finalValueLabel.text = [NSString stringWithFormat:@"最终值:%.0f",slider.value * 100]; 96 | } 97 | 98 | ``` 99 | 100 | ## 四、实现原理 101 | 简单来说就是,整个控件继承与UIControl,根据value,loadProgress的值改变重新绘制layer和改变thumb的位置;根据手势所在的位置,重新绘制layer和改变thumb的位置,并改变value。 102 | ### 1. 根据所给value绘制圆弧 103 | circleSlider.value和circleSlider.loadProgress,都是绘制圆弧。 104 | 105 | ####1)在`- (void)drawRect:(CGRect)rect;`方法中绘制圆弧 106 | 在iOS中,圆的0弧度的位置是圆心(x,y)的正右侧(x+r,y) 107 | 这里我选择起始位置为-M_PI_2弧度,即圆心的正上方(x,y-r); 108 | 弧长对应的变量就是运动的点相对于起点旋转过的角度,而这个角度就等于`value/1.0 * 360` 109 | 下面给出了加载进度圆弧的绘制方法,value的圆弧绘制方法同理 110 | ``` 111 | - (void)drawRect:(CGRect)rect { 112 | //加载的进度 113 | UIBezierPath *loadPath = [UIBezierPath bezierPath]; 114 | CGFloat loadStart = -M_PI_2; 115 | CGFloat loadCurre = loadStart + 2 * M_PI * self.loadProgress; 116 | 117 | [loadPath addArcWithCenter:self.drawCenter 118 | radius:self.radius 119 | startAngle:loadStart 120 | endAngle:loadCurre 121 | clockwise:YES]; 122 | CGContextSaveGState(ctx); 123 | CGContextSetShouldAntialias(ctx, YES); 124 | CGContextSetLineWidth(ctx, self.circleBorderWidth); 125 | CGContextSetStrokeColorWithColor(ctx, self.maximumTrackTintColor.CGColor); 126 | CGContextAddPath(ctx, loadPath.CGPath); 127 | CGContextDrawPath(ctx, kCGPathStroke); 128 | CGContextRestoreGState(ctx); 129 | } 130 | ``` 131 | #### 2) 在值改变的时候重新绘制layer 132 | 在值改变的时候调用`setNeedsDisplay`方法重新绘制layer 133 | 134 | ``` 135 | - (void)setLoadProgress:(float)loadProgress { 136 | _loadProgress = loadProgress; 137 | [self setNeedsDisplay]; 138 | } 139 | 140 | ``` 141 | 142 | ### 2.拖动控制,以及保证thumb(滑块的那个圆点)在圆弧上运动 143 | **这里主要给出了解决的思路方法,具体的实现可到[Github](https://github.com/JixinZhang/ZCircleSlider/)中查看** 144 | 145 | 主要是在UIControl的以下三个方法上做文章: 146 | 147 | ``` 148 | //点击开始 149 | - (BOOL)beginTrackingWithTouch:(UITouch *)touch withEvent:(UIEvent *)event; 150 | 151 | //拖动过程中 152 | - (BOOL)continueTrackingWithTouch:(UITouch *)touch withEvent:(UIEvent *)event; 153 | 154 | //拖动结束 155 | - (void)endTrackingWithTouch:(UITouch *)touch withEvent:(UIEvent *)event; 156 | ``` 157 | 158 | #### 1) 对于拖动控制,以及保证thumb在圆弧上运动可以通过以下这道数学题做抽象 159 | ``` 160 | 1.设定平面直角坐标系原点为O(0,0),向右为x轴的正方向,向下为y轴的正方向;--------(iOS的屏幕坐标系) 161 | 2.在坐标系中有一个已知的圆C(a,b),半径为r;--------(圆环型Slider) 162 | 3.平面内任意一点S(m,n);--------(点击的位置) 163 | 求: 164 | 1)点S到圆C的最短距离是否小于44;(限定点击响应的区域为圆弧内外44个点的区域) 165 | 2)线段SC与圆的交点T(xT,yT);(thumb的位置,肯定在圆弧上) 166 | 3)线段ST的距离是否小于44;(限定点击开始时的响应区域为以thumb圆心,半径为44的圆以内) 167 | 168 | ``` 169 | 170 | #### 2) 对于当value=0%时,滑块不可以再逆时针滑动,当value=100%时,滑块不可以再顺时针滑动。 171 | 172 | ``` 173 | 1. 平面内一个圆C半径为r,其圆心位于坐标系原点,即C(0,0),圆弧上有一点T(x,y); 174 | 2. 坐标系可分为第一、二、三和四象限。 175 | 1)那么当点T相对于起始点(0,-r),顺时针转过的角度<60度时,禁止移动到第二,三,四象限; 176 | 2)当点T相对于起始点(0,-r),顺时针转过角度>300度时,禁止移动到第一,二,三象限; 177 | 178 | ``` 179 | 180 | ### 如果觉得这个ZCircleSlider还不错请随手给个Star吧 181 | -------------------------------------------------------------------------------- /ZCircleSliderDemo/ZCircleSliderDemo/SecondViewController.m: -------------------------------------------------------------------------------- 1 | // 2 | // SecondViewController.m 3 | // ZCircleSliderDemo 4 | // 5 | // Created by ZhangBob on 01/06/2017. 6 | // Copyright © 2017 Jixin. All rights reserved. 7 | // 8 | 9 | #import "SecondViewController.h" 10 | #import "ZCircleSlider.h" 11 | #import "DefaultDefine.h" 12 | 13 | @interface SecondViewController () 14 | 15 | @property (nonatomic, strong) ZCircleSlider *circleSlider; 16 | @property (nonatomic, strong) UILabel *currentValueLabel; 17 | @property (nonatomic, strong) UILabel *finalValueLabel; 18 | @property (nonatomic, strong) UISlider *progressSlider; 19 | 20 | @property (nonatomic, strong) UILabel *progressValueLabel; 21 | @property (nonatomic, strong) UISlider *loadProgressSlider; 22 | 23 | @property (nonatomic, strong) UILabel *valueLabel; 24 | @property (nonatomic, strong) UILabel *progressLabel; 25 | 26 | @end 27 | 28 | @implementation SecondViewController 29 | 30 | - (void)viewDidLoad { 31 | [super viewDidLoad]; 32 | self.view.backgroundColor = [UIColor whiteColor]; 33 | [self.view addSubview:self.circleSlider]; 34 | [self.view addSubview:self.currentValueLabel]; 35 | [self.view addSubview:self.finalValueLabel]; 36 | [self.view addSubview:self.progressValueLabel]; 37 | [self.view addSubview:self.progressSlider]; 38 | [self.view addSubview:self.loadProgressSlider]; 39 | [self.view addSubview:self.valueLabel]; 40 | [self.view addSubview:self.progressLabel]; 41 | } 42 | 43 | - (void)didReceiveMemoryWarning { 44 | [super didReceiveMemoryWarning]; 45 | // Dispose of any resources that can be recreated. 46 | } 47 | 48 | - (ZCircleSlider *)circleSlider { 49 | if (!_circleSlider) { 50 | _circleSlider = [[ZCircleSlider alloc] initWithFrame:CGRectMake((kScreenWidth - 300) / 2.0, (kScreenHeight - 300) / 2.0, 300, 300)]; 51 | _circleSlider.minimumTrackTintColor = kUIColorFromRGB(0x1482f0); 52 | _circleSlider.maximumTrackTintColor = kUIColorFromRGB(0xE62E2E); 53 | _circleSlider.backgroundTintColor = [UIColor colorWithWhite:0 alpha:0.2]; 54 | _circleSlider.circleBorderWidth = 5.0f; 55 | _circleSlider.thumbRadius = 8; 56 | _circleSlider.thumbExpandRadius = 12.5; 57 | _circleSlider.thumbTintColor = [UIColor redColor]; 58 | _circleSlider.circleRadius = 260 / 2.0 + 2; 59 | _circleSlider.value = 0; 60 | _circleSlider.loadProgress = 0; 61 | [_circleSlider addTarget:self 62 | action:@selector(circleSliderTouchDown:) 63 | forControlEvents:UIControlEventTouchDown]; 64 | [_circleSlider addTarget:self 65 | action:@selector(circleSliderValueChanging:) 66 | forControlEvents:UIControlEventValueChanged]; 67 | [_circleSlider addTarget:self 68 | action:@selector(circleSliderValueDidChanged:) 69 | forControlEvents:UIControlEventTouchUpInside]; 70 | } 71 | return _circleSlider; 72 | } 73 | 74 | - (UILabel *)currentValueLabel { 75 | if (!_currentValueLabel) { 76 | _currentValueLabel = [[UILabel alloc] initWithFrame:CGRectMake((kScreenWidth - 100) / 2.0, (kScreenHeight - 30) / 2.0 - 30, 100, 30)]; 77 | _currentValueLabel.textAlignment = NSTextAlignmentCenter; 78 | _currentValueLabel.text = @"当前值:0"; 79 | } 80 | return _currentValueLabel; 81 | } 82 | 83 | - (UILabel *)finalValueLabel { 84 | if (!_finalValueLabel) { 85 | _finalValueLabel = [[UILabel alloc] initWithFrame:CGRectMake((kScreenWidth - 100) / 2.0, (kScreenHeight - 30) / 2.0, 100, 30)]; 86 | _finalValueLabel.textAlignment = NSTextAlignmentCenter; 87 | _finalValueLabel.text = @"最终值:0"; 88 | } 89 | return _finalValueLabel; 90 | } 91 | 92 | - (UISlider *)progressSlider { 93 | if (!_progressSlider) { 94 | _progressSlider = [[UISlider alloc] initWithFrame:CGRectMake(80, kScreenHeight - 40, kScreenWidth - 80 - 20, 30)]; 95 | _progressSlider.backgroundColor = [UIColor clearColor]; 96 | _progressSlider.minimumValue = 0; 97 | _progressSlider.maximumValue = 1; 98 | _progressSlider.value = 0; 99 | _progressSlider.tag = 100; 100 | [_progressSlider addTarget:self 101 | action:@selector(progressSliderValueChanging:) 102 | forControlEvents:UIControlEventValueChanged]; 103 | 104 | [_progressSlider addTarget:self 105 | action:@selector(progressSliderValueDidChanged:) 106 | forControlEvents:UIControlEventTouchUpInside]; 107 | 108 | } 109 | return _progressSlider; 110 | } 111 | 112 | - (UILabel *)progressValueLabel { 113 | if (!_progressValueLabel) { 114 | _progressValueLabel = [[UILabel alloc] initWithFrame:CGRectMake((kScreenWidth - 130) / 2.0, (kScreenHeight - 30) / 2.0 + 30, 130, 30)]; 115 | _progressValueLabel.textAlignment = NSTextAlignmentCenter; 116 | _progressValueLabel.text = @"加载进度:0"; 117 | } 118 | return _progressValueLabel; 119 | } 120 | 121 | - (UISlider *)loadProgressSlider { 122 | if (!_loadProgressSlider) { 123 | _loadProgressSlider = [[UISlider alloc] initWithFrame:CGRectMake(80, kScreenHeight - 80, kScreenWidth - 80 - 20, 30)]; 124 | _loadProgressSlider.backgroundColor = [UIColor clearColor]; 125 | _loadProgressSlider.minimumValue = 0; 126 | _loadProgressSlider.maximumValue = 1; 127 | _loadProgressSlider.value = 0; 128 | _loadProgressSlider.tag = 101; 129 | [_loadProgressSlider addTarget:self 130 | action:@selector(progressSliderValueChanging:) 131 | forControlEvents:UIControlEventValueChanged]; 132 | 133 | [_loadProgressSlider addTarget:self 134 | action:@selector(progressSliderValueDidChanged:) 135 | forControlEvents:UIControlEventTouchUpInside]; 136 | 137 | } 138 | return _loadProgressSlider; 139 | } 140 | 141 | - (UILabel *)progressLabel { 142 | if (!_progressLabel) { 143 | _progressLabel = [[UILabel alloc] initWithFrame:CGRectMake(5, kScreenHeight - 80, 50, 30)]; 144 | _progressLabel.text = @"加载进度:"; 145 | [_progressLabel sizeToFit]; 146 | } 147 | return _progressLabel; 148 | } 149 | 150 | - (UILabel *)valueLabel { 151 | if (!_valueLabel) { 152 | _valueLabel = [[UILabel alloc] initWithFrame:CGRectMake(5, kScreenHeight - 40, 50, 30)]; 153 | _valueLabel.text = @"value:"; 154 | [_valueLabel sizeToFit]; 155 | } 156 | return _valueLabel; 157 | } 158 | 159 | #pragma mark - action 160 | 161 | /*以下三个方法,都要添加对slider.interaction的判断。 162 | *因为虽然看起来是个圆环,但是响应手势的区域确实整个矩形的View 163 | *在内部添加了interaction这个属性用于限定响应区域,在规定的区 164 | */ 165 | 166 | - (IBAction)circleSliderTouchDown:(ZCircleSlider *)slider { 167 | if (!slider.interaction) { 168 | return; 169 | } 170 | 171 | } 172 | 173 | - (IBAction)circleSliderValueChanging:(ZCircleSlider *)slider { 174 | if (!slider.interaction) { 175 | return; 176 | } 177 | self.currentValueLabel.text = [NSString stringWithFormat:@"当前值:%.0f",slider.value * 100]; 178 | self.progressSlider.value = slider.value; 179 | } 180 | 181 | - (IBAction)circleSliderValueDidChanged:(ZCircleSlider *)slider { 182 | if (!slider.interaction) { 183 | return; 184 | } 185 | self.finalValueLabel.text = [NSString stringWithFormat:@"最终值:%.0f",slider.value * 100]; 186 | } 187 | 188 | - (IBAction)progressSliderValueChanging:(UISlider *)slider { 189 | if (slider.tag == 100) { 190 | self.currentValueLabel.text = [NSString stringWithFormat:@"当前值:%.0f",slider.value * 100]; 191 | self.circleSlider.value = slider.value; 192 | } else if (slider.tag == 101) { 193 | self.progressValueLabel.text = [NSString stringWithFormat:@"加载进度:%.0f",slider.value * 100]; 194 | self.circleSlider.loadProgress = slider.value; 195 | } 196 | } 197 | 198 | - (IBAction)progressSliderValueDidChanged:(UISlider *)slider { 199 | if (slider.tag == 100) { 200 | self.progressSlider.value = slider.value; 201 | self.finalValueLabel.text = [NSString stringWithFormat:@"最终值:%.0f",slider.value * 100]; 202 | } else if (slider.tag == 101) { 203 | 204 | } 205 | } 206 | @end 207 | -------------------------------------------------------------------------------- /ZCircleSlider/ZCircleSlider.m: -------------------------------------------------------------------------------- 1 | // 2 | // ZCircleSlider.m 3 | // LoadingView 4 | // 5 | // Created by ZhangBob on 24/05/2017. 6 | // Copyright © 2017 JixinZhang. All rights reserved. 7 | // 8 | 9 | #import "ZCircleSlider.h" 10 | 11 | @interface ZCircleSlider() 12 | 13 | @property (nonatomic, strong) UIImageView *thumbView; 14 | @property (nonatomic, assign) CGPoint lastPoint; //滑块的实时位置 15 | 16 | @property (nonatomic, assign) CGFloat radius; //半径 17 | @property (nonatomic, assign) CGPoint drawCenter; //绘制圆的圆心 18 | @property (nonatomic, assign) CGPoint circleStartPoint; //thumb起始位置 19 | @property (nonatomic, assign) CGFloat angle; //转过的角度 20 | 21 | @property (nonatomic, assign) BOOL lockClockwise; //禁止顺时针转动 22 | @property (nonatomic, assign) BOOL lockAntiClockwise; //禁止逆时针转动 23 | 24 | @property (nonatomic, assign) BOOL interaction; 25 | 26 | @end 27 | 28 | @implementation ZCircleSlider 29 | 30 | - (void)dealloc { 31 | [self removeObserver:self forKeyPath:@"angle"]; 32 | } 33 | 34 | - (instancetype)init { 35 | self = [super init]; 36 | if (self) { 37 | [self setup]; 38 | } 39 | return self; 40 | } 41 | 42 | - (instancetype)initWithFrame:(CGRect)frame { 43 | self = [super initWithFrame:frame]; 44 | if (self) { 45 | [self setup]; 46 | } 47 | return self; 48 | } 49 | 50 | 51 | /** 52 | 设定默认值 53 | */ 54 | - (void)setup { 55 | self.backgroundColor = [UIColor clearColor]; 56 | self.circleRadius = MIN(self.frame.size.width, self.frame.size.height) - 24; 57 | self.circleBorderWidth = 5.0f; 58 | self.thumbRadius = 12.0f; 59 | self.thumbExpandRadius = 25.0f; 60 | self.maximumTrackTintColor = [UIColor lightGrayColor]; 61 | self.minimumTrackTintColor = [UIColor blueColor]; 62 | 63 | self.drawCenter = CGPointMake(self.frame.size.width / 2.0, self.frame.size.height / 2.0); 64 | self.circleStartPoint = CGPointMake(self.drawCenter.x, self.drawCenter.y - self.circleRadius); 65 | self.loadProgress = 1.0; 66 | self.interaction = NO; 67 | self.canRepeat = NO; 68 | self.angle = 0; 69 | self.lockAntiClockwise = YES; 70 | self.lockClockwise = NO; 71 | [self addSubview:self.thumbView]; 72 | 73 | [self addObserver:self 74 | forKeyPath:@"angle" 75 | options:NSKeyValueObservingOptionNew 76 | context:nil]; 77 | } 78 | 79 | #pragma mark - getter 80 | 81 | - (UIImageView *)thumbView { 82 | if (!_thumbView) { 83 | _thumbView = [[UIImageView alloc] initWithFrame:CGRectMake(0, 0, 12, 12)]; 84 | _thumbView.image = [UIImage imageNamed:@"thumbSlider"]; 85 | _thumbView.layer.masksToBounds = YES; 86 | _thumbView.userInteractionEnabled = NO; 87 | 88 | } 89 | return _thumbView; 90 | } 91 | 92 | #pragma mark - setter 93 | 94 | - (void)setValue:(float)value { 95 | if (value < 0.25) { 96 | self.lockClockwise = NO; 97 | } else { 98 | self.lockAntiClockwise = NO; 99 | } 100 | _value = MIN(MAX(value, 0.0), 0.997648); 101 | [self setNeedsDisplay]; 102 | } 103 | 104 | - (void)setLoadProgress:(float)loadProgress { 105 | _loadProgress = loadProgress; 106 | [self setNeedsDisplay]; 107 | } 108 | 109 | - (void)setCanRepeat:(BOOL)canRepeat { 110 | _canRepeat = canRepeat; 111 | [self setNeedsDisplay]; 112 | } 113 | 114 | - (void)setThumbRadius:(CGFloat)thumbRadius { 115 | _thumbRadius = thumbRadius; 116 | self.thumbView.frame = CGRectMake(0, 0, thumbRadius * 2, thumbRadius * 2); 117 | self.thumbView.layer.cornerRadius = thumbRadius; 118 | 119 | [self setNeedsDisplay]; 120 | } 121 | 122 | - (void)setThumbExpandRadius:(CGFloat)thumbExpandRadius { 123 | _thumbExpandRadius = thumbExpandRadius; 124 | [self setNeedsDisplay]; 125 | } 126 | 127 | - (void)setCircleRadius:(CGFloat)circleRadius { 128 | _circleRadius = circleRadius; 129 | self.circleStartPoint = CGPointMake(self.drawCenter.x, self.drawCenter.y - self.circleRadius); 130 | [self setNeedsDisplay]; 131 | } 132 | 133 | - (void)setCircleBorderWidth:(CGFloat)circleBorderWidth { 134 | _circleBorderWidth = circleBorderWidth; 135 | [self setNeedsDisplay]; 136 | } 137 | 138 | - (void)setMinimumTrackTintColor:(UIColor *)minimumTrackTintColor { 139 | _minimumTrackTintColor = minimumTrackTintColor; 140 | [self setNeedsDisplay]; 141 | } 142 | 143 | - (void)setMaximumTrackTintColor:(UIColor *)maximumTrackTintColor { 144 | _maximumTrackTintColor = maximumTrackTintColor; 145 | [self setNeedsDisplay]; 146 | } 147 | 148 | - (void)setThumbTintColor:(UIColor *)thumbTintColor { 149 | _thumbTintColor = thumbTintColor; 150 | self.thumbView.backgroundColor = thumbTintColor; 151 | [self setNeedsDisplay]; 152 | } 153 | 154 | #pragma mark - drwRect 155 | 156 | - (void)drawRect:(CGRect)rect { 157 | self.drawCenter = CGPointMake(self.frame.size.width / 2.0, self.frame.size.height / 2.0); 158 | self.radius = self.circleRadius; 159 | self.circleStartPoint = CGPointMake(self.drawCenter.x, self.drawCenter.y - self.circleRadius); 160 | 161 | CGContextRef ctx = UIGraphicsGetCurrentContext(); 162 | 163 | //圆形的背景颜色 164 | CGContextSetStrokeColorWithColor(ctx, self.backgroundTintColor.CGColor); 165 | CGContextSetLineWidth(ctx, self.circleBorderWidth); 166 | CGContextAddArc(ctx, self.drawCenter.x, self.drawCenter.y, self.radius, 0, 2 * M_PI, 0); 167 | CGContextDrawPath(ctx, kCGPathStroke); 168 | 169 | //加载的进度 170 | UIBezierPath *loadPath = [UIBezierPath bezierPath]; 171 | CGFloat loadStart = -M_PI_2; 172 | CGFloat loadCurre = loadStart + 2 * M_PI * self.loadProgress; 173 | 174 | CGContextSetStrokeColorWithColor(ctx, self.maximumTrackTintColor.CGColor); 175 | CGContextSetLineWidth(ctx, self.circleBorderWidth); 176 | [loadPath addArcWithCenter:self.drawCenter 177 | radius:self.radius 178 | startAngle:loadStart 179 | endAngle:loadCurre 180 | clockwise:YES]; 181 | CGContextAddPath(ctx, loadPath.CGPath); 182 | CGContextDrawPath(ctx, kCGPathStroke); 183 | 184 | //起始位置做圆滑处理 185 | CGContextSaveGState(ctx); 186 | CGContextSetShouldAntialias(ctx, YES); 187 | CGContextSetFillColorWithColor(ctx, self.minimumTrackTintColor.CGColor); 188 | CGContextAddArc(ctx, self.circleStartPoint.x, self.circleStartPoint.y, self.circleBorderWidth / 2.0, 0, M_PI * 2, 0); 189 | CGContextDrawPath(ctx, kCGPathFill); 190 | CGContextRestoreGState(ctx); 191 | 192 | //value 193 | UIBezierPath *circlePath = [UIBezierPath bezierPath]; 194 | CGFloat originstart = -M_PI_2; 195 | CGFloat currentOrigin = originstart + 2 * M_PI * self.value; 196 | [circlePath addArcWithCenter:self.drawCenter 197 | radius:self.radius 198 | startAngle:originstart 199 | endAngle:currentOrigin 200 | clockwise:YES]; 201 | CGContextSaveGState(ctx); 202 | CGContextSetLineWidth(ctx, self.circleBorderWidth); 203 | CGContextSetStrokeColorWithColor(ctx, self.minimumTrackTintColor.CGColor); 204 | CGContextAddPath(ctx, circlePath.CGPath); 205 | CGContextDrawPath(ctx, kCGPathStroke); 206 | CGContextRestoreGState(ctx); 207 | 208 | /* 209 | * 计算移动点的位置 210 | * alpha = 移动点相对于起始点顺时针扫过的角度(弧度) 211 | * x = r * sin(alpha) + 圆心的x坐标, sin在0-PI之间为正,PI-2*PI之间为负 212 | * y 可以通过r * cos(alpha) + 圆心的y坐标来计算。 213 | * 不过我这里用了另外一个比较投机的方法,先算出亮点连线在y轴上投影的长度,然后根据移动点在y轴上相对于圆心的位置将这个绝对长度a和圆心y坐标相加减。 214 | */ 215 | double alpha = self.value * 2 * M_PI; 216 | double x = self.radius * sin(alpha) + self.drawCenter.x; 217 | double y = sqrt(self.radius * self.radius - pow((self.drawCenter.x - x), 2)) + self.drawCenter.y; 218 | double a = y - self.drawCenter.y; 219 | if (self.value <= 0.25 || self.value > 0.75) { 220 | y = self.drawCenter.y - a; 221 | } 222 | self.lastPoint = CGPointMake(x, y); 223 | self.thumbView.center = self.lastPoint; 224 | } 225 | 226 | #pragma mark - UIControl methods 227 | 228 | //点击开始 229 | - (BOOL)beginTrackingWithTouch:(UITouch *)touch withEvent:(UIEvent *)event { 230 | [super beginTrackingWithTouch:touch withEvent:event]; 231 | CGPoint starTouchPoint = [touch locationInView:self]; 232 | 233 | //如果点击点和上一次点击点的距离大于44,不做操作。 234 | double touchDist = [ZCircleSlider distanceBetweenPointA:starTouchPoint pointB:self.lastPoint]; 235 | if (touchDist > 44) { 236 | self.interaction = NO; 237 | [self sendActionsForControlEvents:UIControlEventValueChanged]; 238 | return YES; 239 | } 240 | //如果点击点和圆心的距离大于44,不做操作。 241 | //以上两步是用来限定滑块的点击范围,距离滑块太远不操作,距离圆心太远或太近不操作 242 | double dist = [ZCircleSlider distanceBetweenPointA:starTouchPoint pointB:self.drawCenter]; 243 | if (fabs(dist - self.radius) > 44) { 244 | self.interaction = NO; 245 | [self sendActionsForControlEvents:UIControlEventValueChanged]; 246 | return YES; 247 | } 248 | self.thumbView.center = self.lastPoint; 249 | //点击后滑块放大及动画 250 | CGFloat expandRate = self.thumbExpandRadius / self.thumbRadius; 251 | __weak typeof (self)weakSelf = self; 252 | [UIView animateWithDuration:0.15 253 | animations:^{ 254 | weakSelf.thumbView.transform = CGAffineTransformMakeScale(1.0f * expandRate, 1.0f * expandRate); 255 | }]; 256 | [self moveHandlerWithPoint:starTouchPoint]; 257 | [self sendActionsForControlEvents:UIControlEventValueChanged]; 258 | return YES; 259 | } 260 | 261 | //拖动过程中 262 | - (BOOL)continueTrackingWithTouch:(UITouch *)touch withEvent:(UIEvent *)event { 263 | [super continueTrackingWithTouch:touch withEvent:event]; 264 | CGPoint starTouchPoint = [touch locationInView:self]; 265 | 266 | double touchDist = [ZCircleSlider distanceBetweenPointA:starTouchPoint pointB:self.lastPoint]; 267 | if (touchDist > 44) { 268 | [self sendActionsForControlEvents:UIControlEventValueChanged]; 269 | return YES; 270 | } 271 | double dist = [ZCircleSlider distanceBetweenPointA:starTouchPoint pointB:self.drawCenter]; 272 | if (fabs(dist - self.radius) > 44) { 273 | [self sendActionsForControlEvents:UIControlEventValueChanged]; 274 | return YES; 275 | } 276 | [self moveHandlerWithPoint:starTouchPoint]; 277 | [self sendActionsForControlEvents:UIControlEventValueChanged]; 278 | return YES; 279 | } 280 | 281 | //拖动结束 282 | - (void)endTrackingWithTouch:(UITouch *)touch withEvent:(UIEvent *)event { 283 | [super endTrackingWithTouch:touch withEvent:event]; 284 | self.thumbView.center = self.lastPoint; 285 | __weak typeof (self)weakSelf = self; 286 | [UIView animateWithDuration:0.15 287 | animations:^{ 288 | weakSelf.thumbView.transform = CGAffineTransformMakeScale(1.0f, 1.0f); 289 | }]; 290 | 291 | CGPoint starTouchPoint = [touch locationInView:self]; 292 | 293 | double touchDist = [ZCircleSlider distanceBetweenPointA:starTouchPoint pointB:self.lastPoint]; 294 | if (touchDist > 44) { 295 | [self sendActionsForControlEvents:UIControlEventEditingDidEnd]; 296 | return; 297 | } 298 | double dist = [ZCircleSlider distanceBetweenPointA:starTouchPoint pointB:self.drawCenter]; 299 | if (fabs(dist - self.radius) > 44) { 300 | [self sendActionsForControlEvents:UIControlEventEditingDidEnd]; 301 | return; 302 | } 303 | [self moveHandlerWithPoint:starTouchPoint]; 304 | [self sendActionsForControlEvents:UIControlEventEditingDidEnd]; 305 | } 306 | 307 | - (void)moveHandlerWithPoint:(CGPoint)point { 308 | self.interaction = YES; 309 | CGFloat centerX = self.drawCenter.x; 310 | CGFloat centerY = self.drawCenter.y; 311 | 312 | CGFloat moveX = point.x; 313 | CGFloat moveY = point.y; 314 | 315 | if (!self.canRepeat) { 316 | //到300度,禁止移动到第一,二,三象限 317 | if (self.lockClockwise) { 318 | if ((moveX >= centerX && moveY <= centerY) || 319 | (moveX >= centerX && moveY >= centerY) || 320 | (moveX <= centerX && moveY >= centerY)) { 321 | return; 322 | } 323 | } 324 | 325 | //小于60度的时候,禁止移动到第二,三,四象限 326 | if (self.lockAntiClockwise) { 327 | if ((moveX <= centerX && moveY >= centerY) || 328 | (moveX <= centerX && moveY <= centerY) || 329 | (moveX >= centerX && moveY >= centerY)) { 330 | return; 331 | } 332 | } 333 | } 334 | 335 | double dist = sqrt(pow((moveX - centerY), 2) + pow(moveY - centerY, 2)); 336 | if (fabs(dist - self.radius) > 44) { 337 | return; 338 | } 339 | /* 340 | * 计算移动点的坐标 341 | * sinAlpha = 亮点在x轴上投影的长度 / 距离 342 | * xT = r * sin(alpha) + 圆心的x坐标 343 | * yT 算法同上 344 | */ 345 | double sinAlpha = (moveX - centerX) / dist; 346 | double xT = self.radius * sinAlpha + centerX; 347 | double yT = sqrt((self.radius * self.radius - (xT - centerX) * (xT - centerX))) + centerY; 348 | if (moveY < centerY) { 349 | yT = centerY - fabs(yT - centerY); 350 | } 351 | self.lastPoint = self.thumbView.center = CGPointMake(xT, yT); 352 | 353 | CGFloat angle = [ZCircleSlider calculateAngleWithRadius:self.radius 354 | center:self.drawCenter 355 | startCenter:self.circleStartPoint 356 | endCenter:self.lastPoint]; 357 | if (angle >= 300) { 358 | //当当前角度大于等于300度时禁止移动到第一、二、三象限 359 | self.lockClockwise = YES; 360 | } else { 361 | self.lockClockwise = NO; 362 | } 363 | 364 | if (angle <= 60.0) { 365 | //当当前角度小于等于60度时,禁止移动到第二、三、四象限 366 | self.lockAntiClockwise = YES; 367 | } else { 368 | self.lockAntiClockwise = NO; 369 | } 370 | self.angle = angle; 371 | self.value = angle / 360; 372 | } 373 | 374 | 375 | /** 376 | 计算圆上两点间的角度 377 | 378 | @param radius 半径 379 | @param center 圆心 380 | @param startCenter 起始点坐标 381 | @param endCenter 结束点坐标 382 | @return 圆上两点间的角度 383 | */ 384 | + (CGFloat)calculateAngleWithRadius:(CGFloat)radius 385 | center:(CGPoint)center 386 | startCenter:(CGPoint)startCenter 387 | endCenter:(CGPoint)endCenter { 388 | //a^2 = b^2 + c^2 - 2bccosA; 389 | CGFloat cosA = (2 * radius * radius - powf([ZCircleSlider distanceBetweenPointA:startCenter pointB:endCenter], 2)) / (2 * radius * radius); 390 | CGFloat angle = 180 / M_PI * acosf(cosA); 391 | if (startCenter.x > endCenter.x) { 392 | angle = 360 - angle; 393 | } 394 | return angle; 395 | } 396 | 397 | /** 398 | 两点间的距离 399 | 400 | @param pointA 点A的坐标 401 | @param pointB 点B的坐标 402 | @return 两点间的距离 403 | */ 404 | + (double)distanceBetweenPointA:(CGPoint)pointA pointB:(CGPoint)pointB { 405 | double x = fabs(pointA.x - pointB.x); 406 | double y = fabs(pointA.y - pointB.y); 407 | return hypot(x, y);//hypot(x, y)函数为计算三角形的斜边长度 408 | } 409 | 410 | #pragma mark - KVO 411 | 412 | //对angle添加KVO,有时候手势过快在continueTrackingWithTouch方法中不能及时限定转动,所以需要通过KVO对angle做实时监控 413 | - (void)observeValueForKeyPath:(NSString *)keyPath ofObject:(id)object change:(NSDictionary *)change context:(void *)context { 414 | ZCircleSlider *circleSlider = (ZCircleSlider *)object; 415 | NSNumber *newAngle = [change valueForKey:@"new"]; 416 | if ([keyPath isEqualToString:@"angle"]) { 417 | if (newAngle.doubleValue >= 300 || 418 | circleSlider.angle >= 300) { 419 | self.lockClockwise = YES; 420 | } else { 421 | self.lockClockwise = NO; 422 | } 423 | 424 | if (newAngle.doubleValue <= 60 || 425 | circleSlider.angle <= 60) { 426 | self.lockAntiClockwise = YES; 427 | } else { 428 | self.lockAntiClockwise = NO; 429 | } 430 | } 431 | } 432 | 433 | @end 434 | -------------------------------------------------------------------------------- /ZCircleSliderDemo/ZCircleSliderDemo/ZCircleSlider/ZCircleSlider.m: -------------------------------------------------------------------------------- 1 | // 2 | // ZCircleSlider.m 3 | // LoadingView 4 | // 5 | // Created by ZhangBob on 24/05/2017. 6 | // Copyright © 2017 JixinZhang. All rights reserved. 7 | // 8 | 9 | #import "ZCircleSlider.h" 10 | 11 | @interface ZCircleSlider() 12 | 13 | @property (nonatomic, strong) UIImageView *thumbView; 14 | @property (nonatomic, assign) CGPoint lastPoint; //滑块的实时位置 15 | 16 | @property (nonatomic, assign) CGFloat radius; //半径 17 | @property (nonatomic, assign) CGPoint drawCenter; //绘制圆的圆心 18 | @property (nonatomic, assign) CGPoint circleStartPoint; //thumb起始位置 19 | @property (nonatomic, assign) CGFloat angle; //转过的角度 20 | 21 | @property (nonatomic, assign) BOOL lockClockwise; //禁止顺时针转动 22 | @property (nonatomic, assign) BOOL lockAntiClockwise; //禁止逆时针转动 23 | 24 | @property (nonatomic, assign) BOOL interaction; 25 | 26 | @end 27 | 28 | @implementation ZCircleSlider 29 | 30 | - (void)dealloc { 31 | [self removeObserver:self forKeyPath:@"angle"]; 32 | } 33 | 34 | - (instancetype)init { 35 | self = [super init]; 36 | if (self) { 37 | [self setup]; 38 | } 39 | return self; 40 | } 41 | 42 | - (instancetype)initWithFrame:(CGRect)frame { 43 | self = [super initWithFrame:frame]; 44 | if (self) { 45 | [self setup]; 46 | } 47 | return self; 48 | } 49 | 50 | 51 | /** 52 | 设定默认值 53 | */ 54 | - (void)setup { 55 | self.backgroundColor = [UIColor clearColor]; 56 | self.circleRadius = MIN(self.frame.size.width, self.frame.size.height) - 24; 57 | self.circleBorderWidth = 5.0f; 58 | self.thumbRadius = 12.0f; 59 | self.thumbExpandRadius = 25.0f; 60 | self.maximumTrackTintColor = [UIColor lightGrayColor]; 61 | self.minimumTrackTintColor = [UIColor blueColor]; 62 | 63 | self.drawCenter = CGPointMake(self.frame.size.width / 2.0, self.frame.size.height / 2.0); 64 | self.circleStartPoint = CGPointMake(self.drawCenter.x, self.drawCenter.y - self.circleRadius); 65 | self.loadProgress = 1.0; 66 | self.interaction = NO; 67 | self.canRepeat = NO; 68 | self.angle = 0; 69 | self.lockAntiClockwise = YES; 70 | self.lockClockwise = NO; 71 | [self addSubview:self.thumbView]; 72 | 73 | [self addObserver:self 74 | forKeyPath:@"angle" 75 | options:NSKeyValueObservingOptionNew 76 | context:nil]; 77 | } 78 | 79 | #pragma mark - getter 80 | 81 | - (UIImageView *)thumbView { 82 | if (!_thumbView) { 83 | _thumbView = [[UIImageView alloc] initWithFrame:CGRectMake(0, 0, 12, 12)]; 84 | _thumbView.image = [UIImage imageNamed:@"thumbSlider"]; 85 | _thumbView.layer.masksToBounds = YES; 86 | _thumbView.userInteractionEnabled = NO; 87 | 88 | } 89 | return _thumbView; 90 | } 91 | 92 | #pragma mark - setter 93 | 94 | - (void)setValue:(float)value { 95 | if (value < 0.25) { 96 | self.lockClockwise = NO; 97 | } else { 98 | self.lockAntiClockwise = NO; 99 | } 100 | _value = MIN(MAX(value, 0.0), 0.997648); 101 | [self setNeedsDisplay]; 102 | } 103 | 104 | - (void)setLoadProgress:(float)loadProgress { 105 | _loadProgress = loadProgress; 106 | [self setNeedsDisplay]; 107 | } 108 | 109 | - (void)setCanRepeat:(BOOL)canRepeat { 110 | _canRepeat = canRepeat; 111 | [self setNeedsDisplay]; 112 | } 113 | 114 | - (void)setThumbRadius:(CGFloat)thumbRadius { 115 | _thumbRadius = thumbRadius; 116 | self.thumbView.frame = CGRectMake(0, 0, thumbRadius * 2, thumbRadius * 2); 117 | self.thumbView.layer.cornerRadius = thumbRadius; 118 | 119 | [self setNeedsDisplay]; 120 | } 121 | 122 | - (void)setThumbExpandRadius:(CGFloat)thumbExpandRadius { 123 | _thumbExpandRadius = thumbExpandRadius; 124 | [self setNeedsDisplay]; 125 | } 126 | 127 | - (void)setCircleRadius:(CGFloat)circleRadius { 128 | _circleRadius = circleRadius; 129 | self.circleStartPoint = CGPointMake(self.drawCenter.x, self.drawCenter.y - self.circleRadius); 130 | [self setNeedsDisplay]; 131 | } 132 | 133 | - (void)setCircleBorderWidth:(CGFloat)circleBorderWidth { 134 | _circleBorderWidth = circleBorderWidth; 135 | [self setNeedsDisplay]; 136 | } 137 | 138 | - (void)setMinimumTrackTintColor:(UIColor *)minimumTrackTintColor { 139 | _minimumTrackTintColor = minimumTrackTintColor; 140 | [self setNeedsDisplay]; 141 | } 142 | 143 | - (void)setMaximumTrackTintColor:(UIColor *)maximumTrackTintColor { 144 | _maximumTrackTintColor = maximumTrackTintColor; 145 | [self setNeedsDisplay]; 146 | } 147 | 148 | - (void)setThumbTintColor:(UIColor *)thumbTintColor { 149 | _thumbTintColor = thumbTintColor; 150 | self.thumbView.backgroundColor = thumbTintColor; 151 | [self setNeedsDisplay]; 152 | } 153 | 154 | #pragma mark - drwRect 155 | 156 | - (void)drawRect:(CGRect)rect { 157 | self.drawCenter = CGPointMake(self.frame.size.width / 2.0, self.frame.size.height / 2.0); 158 | self.radius = self.circleRadius; 159 | self.circleStartPoint = CGPointMake(self.drawCenter.x, self.drawCenter.y - self.circleRadius); 160 | 161 | CGContextRef ctx = UIGraphicsGetCurrentContext(); 162 | 163 | //圆形的背景颜色 164 | CGContextSetStrokeColorWithColor(ctx, self.backgroundTintColor.CGColor); 165 | CGContextSetLineWidth(ctx, self.circleBorderWidth); 166 | CGContextAddArc(ctx, self.drawCenter.x, self.drawCenter.y, self.radius, 0, 2 * M_PI, 0); 167 | CGContextDrawPath(ctx, kCGPathStroke); 168 | 169 | //加载的进度 170 | UIBezierPath *loadPath = [UIBezierPath bezierPath]; 171 | CGFloat loadStart = -M_PI_2; 172 | CGFloat loadCurre = loadStart + 2 * M_PI * self.loadProgress; 173 | 174 | [loadPath addArcWithCenter:self.drawCenter 175 | radius:self.radius 176 | startAngle:loadStart 177 | endAngle:loadCurre 178 | clockwise:YES]; 179 | CGContextSaveGState(ctx); 180 | CGContextSetShouldAntialias(ctx, YES); 181 | CGContextSetLineWidth(ctx, self.circleBorderWidth); 182 | CGContextSetStrokeColorWithColor(ctx, self.maximumTrackTintColor.CGColor); 183 | CGContextAddPath(ctx, loadPath.CGPath); 184 | CGContextDrawPath(ctx, kCGPathStroke); 185 | CGContextRestoreGState(ctx); 186 | 187 | //起始位置做圆滑处理 188 | CGContextSaveGState(ctx); 189 | CGContextSetShouldAntialias(ctx, YES); 190 | CGContextSetFillColorWithColor(ctx, self.minimumTrackTintColor.CGColor); 191 | CGContextAddArc(ctx, self.circleStartPoint.x, self.circleStartPoint.y, self.circleBorderWidth / 2.0, 0, M_PI * 2, 0); 192 | CGContextDrawPath(ctx, kCGPathFill); 193 | CGContextRestoreGState(ctx); 194 | 195 | //value 196 | UIBezierPath *circlePath = [UIBezierPath bezierPath]; 197 | CGFloat originstart = -M_PI_2; 198 | CGFloat currentOrigin = originstart + 2 * M_PI * self.value; 199 | [circlePath addArcWithCenter:self.drawCenter 200 | radius:self.radius 201 | startAngle:originstart 202 | endAngle:currentOrigin 203 | clockwise:YES]; 204 | CGContextSaveGState(ctx); 205 | CGContextSetShouldAntialias(ctx, YES); 206 | CGContextSetLineWidth(ctx, self.circleBorderWidth); 207 | CGContextSetStrokeColorWithColor(ctx, self.minimumTrackTintColor.CGColor); 208 | CGContextAddPath(ctx, circlePath.CGPath); 209 | CGContextDrawPath(ctx, kCGPathStroke); 210 | CGContextRestoreGState(ctx); 211 | 212 | /* 213 | * 计算移动点的位置 214 | * alpha = 移动点相对于起始点顺时针扫过的角度(弧度) 215 | * x = r * sin(alpha) + 圆心的x坐标, sin在0-PI之间为正,PI-2*PI之间为负 216 | * y 可以通过r * cos(alpha) + 圆心的y坐标来计算。 217 | * 不过我这里用了另外一个比较投机的方法,先算出亮点连线在y轴上投影的长度,然后根据移动点在y轴上相对于圆心的位置将这个绝对长度a和圆心y坐标相加减。 218 | */ 219 | double alpha = self.value * 2 * M_PI; 220 | double x = self.radius * sin(alpha) + self.drawCenter.x; 221 | double y = sqrt(self.radius * self.radius - pow((self.drawCenter.x - x), 2)) + self.drawCenter.y; 222 | double a = y - self.drawCenter.y; 223 | if (self.value <= 0.25 || self.value > 0.75) { 224 | y = self.drawCenter.y - a; 225 | } 226 | self.lastPoint = CGPointMake(x, y); 227 | self.thumbView.center = self.lastPoint; 228 | } 229 | 230 | #pragma mark - UIControl methods 231 | 232 | //点击开始 233 | - (BOOL)beginTrackingWithTouch:(UITouch *)touch withEvent:(UIEvent *)event { 234 | [super beginTrackingWithTouch:touch withEvent:event]; 235 | CGPoint starTouchPoint = [touch locationInView:self]; 236 | 237 | //如果点击点和上一次点击点的距离大于44,不做操作。 238 | double touchDist = [ZCircleSlider distanceBetweenPointA:starTouchPoint pointB:self.lastPoint]; 239 | if (touchDist > 44) { 240 | self.interaction = NO; 241 | [self sendActionsForControlEvents:UIControlEventValueChanged]; 242 | return YES; 243 | } 244 | //如果点击点和圆心的距离大于44,不做操作。 245 | //以上两步是用来限定滑块的点击范围,距离滑块太远不操作,距离圆心太远或太近不操作 246 | // double dist = [ZCircleSlider distanceBetweenPointA:starTouchPoint pointB:self.drawCenter]; 247 | // if (fabs(dist - self.radius) > 44) { 248 | // self.interaction = NO; 249 | // [self sendActionsForControlEvents:UIControlEventValueChanged]; 250 | // return YES; 251 | // } 252 | self.thumbView.center = self.lastPoint; 253 | //点击后滑块放大及动画 254 | CGFloat expandRate = self.thumbExpandRadius / self.thumbRadius; 255 | __weak typeof (self)weakSelf = self; 256 | [UIView animateWithDuration:0.15 257 | animations:^{ 258 | weakSelf.thumbView.transform = CGAffineTransformMakeScale(1.0f * expandRate, 1.0f * expandRate); 259 | }]; 260 | [self moveHandlerWithPoint:starTouchPoint]; 261 | [self sendActionsForControlEvents:UIControlEventValueChanged]; 262 | return YES; 263 | } 264 | 265 | //拖动过程中 266 | - (BOOL)continueTrackingWithTouch:(UITouch *)touch withEvent:(UIEvent *)event { 267 | [super continueTrackingWithTouch:touch withEvent:event]; 268 | CGPoint starTouchPoint = [touch locationInView:self]; 269 | 270 | // double touchDist = [ZCircleSlider distanceBetweenPointA:starTouchPoint pointB:self.lastPoint]; 271 | // if (touchDist > 44) { 272 | // [self sendActionsForControlEvents:UIControlEventValueChanged]; 273 | // return YES; 274 | // } 275 | // double dist = [ZCircleSlider distanceBetweenPointA:starTouchPoint pointB:self.drawCenter]; 276 | // if (fabs(dist - self.radius) > 44) { 277 | // [self sendActionsForControlEvents:UIControlEventValueChanged]; 278 | // return YES; 279 | // } 280 | [self moveHandlerWithPoint:starTouchPoint]; 281 | [self sendActionsForControlEvents:UIControlEventValueChanged]; 282 | return YES; 283 | } 284 | 285 | //拖动结束 286 | - (void)endTrackingWithTouch:(UITouch *)touch withEvent:(UIEvent *)event { 287 | [super endTrackingWithTouch:touch withEvent:event]; 288 | self.thumbView.center = self.lastPoint; 289 | __weak typeof (self)weakSelf = self; 290 | [UIView animateWithDuration:0.15 291 | animations:^{ 292 | weakSelf.thumbView.transform = CGAffineTransformMakeScale(1.0f, 1.0f); 293 | }]; 294 | 295 | CGPoint starTouchPoint = [touch locationInView:self]; 296 | 297 | // double touchDist = [ZCircleSlider distanceBetweenPointA:starTouchPoint pointB:self.lastPoint]; 298 | // if (touchDist > 44) { 299 | // [self sendActionsForControlEvents:UIControlEventEditingDidEnd]; 300 | // return; 301 | // } 302 | // double dist = [ZCircleSlider distanceBetweenPointA:starTouchPoint pointB:self.drawCenter]; 303 | // if (fabs(dist - self.radius) > 44) { 304 | // [self sendActionsForControlEvents:UIControlEventEditingDidEnd]; 305 | // return; 306 | // } 307 | [self moveHandlerWithPoint:starTouchPoint]; 308 | [self sendActionsForControlEvents:UIControlEventEditingDidEnd]; 309 | } 310 | 311 | #pragma mark - Handle move 312 | 313 | - (void)moveHandlerWithPoint:(CGPoint)point { 314 | self.interaction = YES; 315 | CGFloat centerX = self.drawCenter.x; 316 | CGFloat centerY = self.drawCenter.y; 317 | 318 | CGFloat moveX = point.x; 319 | CGFloat moveY = point.y; 320 | 321 | if (!self.canRepeat) { 322 | //到300度,禁止移动到第一,二,三象限 323 | if (self.lockClockwise) { 324 | if ((moveX >= centerX && moveY <= centerY) || 325 | (moveX >= centerX && moveY >= centerY) || 326 | (moveX <= centerX && moveY >= centerY)) { 327 | return; 328 | } 329 | } 330 | 331 | //小于60度的时候,禁止移动到第二,三,四象限 332 | if (self.lockAntiClockwise) { 333 | if ((moveX <= centerX && moveY >= centerY) || 334 | (moveX <= centerX && moveY <= centerY) || 335 | (moveX >= centerX && moveY >= centerY)) { 336 | return; 337 | } 338 | } 339 | } 340 | 341 | double dist = sqrt(pow((moveX - centerY), 2) + pow(moveY - centerY, 2)); 342 | if ((dist - self.radius) < -64) { 343 | NSLog(@"===== %.3f ",dist - self.radius); 344 | return; 345 | } 346 | /* 347 | * 计算移动点的坐标 348 | * sinAlpha = 亮点在x轴上投影的长度 / 距离 349 | * xT = r * sin(alpha) + 圆心的x坐标 350 | * yT 算法同上 351 | */ 352 | double sinAlpha = (moveX - centerX) / dist; 353 | double xT = self.radius * sinAlpha + centerX; 354 | double yT = sqrt((self.radius * self.radius - (xT - centerX) * (xT - centerX))) + centerY; 355 | if (moveY < centerY) { 356 | yT = centerY - fabs(yT - centerY); 357 | } 358 | self.lastPoint = self.thumbView.center = CGPointMake(xT, yT); 359 | 360 | CGFloat angle = [ZCircleSlider calculateAngleWithRadius:self.radius 361 | center:self.drawCenter 362 | startCenter:self.circleStartPoint 363 | endCenter:self.lastPoint]; 364 | if (angle >= 300) { 365 | //当当前角度大于等于300度时禁止移动到第一、二、三象限 366 | self.lockClockwise = YES; 367 | } else { 368 | self.lockClockwise = NO; 369 | } 370 | 371 | if (angle <= 60.0) { 372 | //当当前角度小于等于60度时,禁止移动到第二、三、四象限 373 | self.lockAntiClockwise = YES; 374 | } else { 375 | self.lockAntiClockwise = NO; 376 | } 377 | self.angle = angle; 378 | self.value = angle / 360; 379 | } 380 | 381 | #pragma mark - Util 382 | 383 | /** 384 | 计算圆上两点间的角度 385 | 386 | @param radius 半径 387 | @param center 圆心 388 | @param startCenter 起始点坐标 389 | @param endCenter 结束点坐标 390 | @return 圆上两点间的角度 391 | */ 392 | + (CGFloat)calculateAngleWithRadius:(CGFloat)radius 393 | center:(CGPoint)center 394 | startCenter:(CGPoint)startCenter 395 | endCenter:(CGPoint)endCenter { 396 | //a^2 = b^2 + c^2 - 2bccosA; 397 | CGFloat cosA = (2 * radius * radius - powf([ZCircleSlider distanceBetweenPointA:startCenter pointB:endCenter], 2)) / (2 * radius * radius); 398 | CGFloat angle = 180 / M_PI * acosf(cosA); 399 | if (startCenter.x > endCenter.x) { 400 | angle = 360 - angle; 401 | } 402 | return angle; 403 | } 404 | 405 | /** 406 | 两点间的距离 407 | 408 | @param pointA 点A的坐标 409 | @param pointB 点B的坐标 410 | @return 两点间的距离 411 | */ 412 | + (double)distanceBetweenPointA:(CGPoint)pointA pointB:(CGPoint)pointB { 413 | double x = fabs(pointA.x - pointB.x); 414 | double y = fabs(pointA.y - pointB.y); 415 | return hypot(x, y);//hypot(x, y)函数为计算三角形的斜边长度 416 | } 417 | 418 | #pragma mark - KVO 419 | 420 | //对angle添加KVO,有时候手势过快在continueTrackingWithTouch方法中不能及时限定转动,所以需要通过KVO对angle做实时监控 421 | - (void)observeValueForKeyPath:(NSString *)keyPath ofObject:(id)object change:(NSDictionary *)change context:(void *)context { 422 | ZCircleSlider *circleSlider = (ZCircleSlider *)object; 423 | NSNumber *newAngle = [change valueForKey:@"new"]; 424 | if ([keyPath isEqualToString:@"angle"]) { 425 | if (newAngle.doubleValue >= 300 || 426 | circleSlider.angle >= 300) { 427 | self.lockClockwise = YES; 428 | } else { 429 | self.lockClockwise = NO; 430 | } 431 | 432 | if (newAngle.doubleValue <= 60 || 433 | circleSlider.angle <= 60) { 434 | self.lockAntiClockwise = YES; 435 | } else { 436 | self.lockAntiClockwise = NO; 437 | } 438 | } 439 | } 440 | 441 | @end 442 | -------------------------------------------------------------------------------- /ZCircleSliderDemo/ZCircleSliderDemo.xcodeproj/project.pbxproj: -------------------------------------------------------------------------------- 1 | // !$*UTF8*$! 2 | { 3 | archiveVersion = 1; 4 | classes = { 5 | }; 6 | objectVersion = 46; 7 | objects = { 8 | 9 | /* Begin PBXBuildFile section */ 10 | 78F86A531EE0225C0016F81E /* main.m in Sources */ = {isa = PBXBuildFile; fileRef = 78F86A521EE0225C0016F81E /* main.m */; }; 11 | 78F86A561EE0225C0016F81E /* AppDelegate.m in Sources */ = {isa = PBXBuildFile; fileRef = 78F86A551EE0225C0016F81E /* AppDelegate.m */; }; 12 | 78F86A591EE0225C0016F81E /* ViewController.m in Sources */ = {isa = PBXBuildFile; fileRef = 78F86A581EE0225C0016F81E /* ViewController.m */; }; 13 | 78F86A5C1EE0225C0016F81E /* Main.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 78F86A5A1EE0225C0016F81E /* Main.storyboard */; }; 14 | 78F86A5E1EE0225C0016F81E /* Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 78F86A5D1EE0225C0016F81E /* Assets.xcassets */; }; 15 | 78F86A611EE0225C0016F81E /* LaunchScreen.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 78F86A5F1EE0225C0016F81E /* LaunchScreen.storyboard */; }; 16 | 78F86A6B1EE023F20016F81E /* MainTableViewController.m in Sources */ = {isa = PBXBuildFile; fileRef = 78F86A6A1EE023F20016F81E /* MainTableViewController.m */; }; 17 | 78F86A6F1EE026CD0016F81E /* ZCircleSlider.m in Sources */ = {isa = PBXBuildFile; fileRef = 78F86A6E1EE026CD0016F81E /* ZCircleSlider.m */; }; 18 | 78F86A721EE026E70016F81E /* FirstViewController.m in Sources */ = {isa = PBXBuildFile; fileRef = 78F86A711EE026E70016F81E /* FirstViewController.m */; }; 19 | 78F86A751EE026F30016F81E /* SecondViewController.m in Sources */ = {isa = PBXBuildFile; fileRef = 78F86A741EE026F30016F81E /* SecondViewController.m */; }; 20 | /* End PBXBuildFile section */ 21 | 22 | /* Begin PBXFileReference section */ 23 | 78F86A4E1EE0225C0016F81E /* ZCircleSliderDemo.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = ZCircleSliderDemo.app; sourceTree = BUILT_PRODUCTS_DIR; }; 24 | 78F86A521EE0225C0016F81E /* main.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = main.m; sourceTree = ""; }; 25 | 78F86A541EE0225C0016F81E /* AppDelegate.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = AppDelegate.h; sourceTree = ""; }; 26 | 78F86A551EE0225C0016F81E /* AppDelegate.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = AppDelegate.m; sourceTree = ""; }; 27 | 78F86A571EE0225C0016F81E /* ViewController.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = ViewController.h; sourceTree = ""; }; 28 | 78F86A581EE0225C0016F81E /* ViewController.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = ViewController.m; sourceTree = ""; }; 29 | 78F86A5B1EE0225C0016F81E /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; name = Base; path = Base.lproj/Main.storyboard; sourceTree = ""; }; 30 | 78F86A5D1EE0225C0016F81E /* Assets.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = Assets.xcassets; sourceTree = ""; }; 31 | 78F86A601EE0225C0016F81E /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; name = Base; path = Base.lproj/LaunchScreen.storyboard; sourceTree = ""; }; 32 | 78F86A621EE0225C0016F81E /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; }; 33 | 78F86A691EE023F20016F81E /* MainTableViewController.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = MainTableViewController.h; sourceTree = ""; }; 34 | 78F86A6A1EE023F20016F81E /* MainTableViewController.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = MainTableViewController.m; sourceTree = ""; }; 35 | 78F86A6D1EE026CD0016F81E /* ZCircleSlider.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = ZCircleSlider.h; sourceTree = ""; }; 36 | 78F86A6E1EE026CD0016F81E /* ZCircleSlider.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = ZCircleSlider.m; sourceTree = ""; }; 37 | 78F86A701EE026E70016F81E /* FirstViewController.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = FirstViewController.h; sourceTree = ""; }; 38 | 78F86A711EE026E70016F81E /* FirstViewController.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = FirstViewController.m; sourceTree = ""; }; 39 | 78F86A731EE026F30016F81E /* SecondViewController.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = SecondViewController.h; sourceTree = ""; }; 40 | 78F86A741EE026F30016F81E /* SecondViewController.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = SecondViewController.m; sourceTree = ""; }; 41 | 78F86A761EE027E10016F81E /* DefaultDefine.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = DefaultDefine.h; sourceTree = ""; }; 42 | /* End PBXFileReference section */ 43 | 44 | /* Begin PBXFrameworksBuildPhase section */ 45 | 78F86A4B1EE0225C0016F81E /* Frameworks */ = { 46 | isa = PBXFrameworksBuildPhase; 47 | buildActionMask = 2147483647; 48 | files = ( 49 | ); 50 | runOnlyForDeploymentPostprocessing = 0; 51 | }; 52 | /* End PBXFrameworksBuildPhase section */ 53 | 54 | /* Begin PBXGroup section */ 55 | 78F86A451EE0225C0016F81E = { 56 | isa = PBXGroup; 57 | children = ( 58 | 78F86A501EE0225C0016F81E /* ZCircleSliderDemo */, 59 | 78F86A4F1EE0225C0016F81E /* Products */, 60 | ); 61 | sourceTree = ""; 62 | }; 63 | 78F86A4F1EE0225C0016F81E /* Products */ = { 64 | isa = PBXGroup; 65 | children = ( 66 | 78F86A4E1EE0225C0016F81E /* ZCircleSliderDemo.app */, 67 | ); 68 | name = Products; 69 | sourceTree = ""; 70 | }; 71 | 78F86A501EE0225C0016F81E /* ZCircleSliderDemo */ = { 72 | isa = PBXGroup; 73 | children = ( 74 | 78F86A6C1EE026CD0016F81E /* ZCircleSlider */, 75 | 78F86A681EE023D40016F81E /* ZCircleSliderDemo */, 76 | 78F86A511EE0225C0016F81E /* Supporting Files */, 77 | ); 78 | path = ZCircleSliderDemo; 79 | sourceTree = ""; 80 | }; 81 | 78F86A511EE0225C0016F81E /* Supporting Files */ = { 82 | isa = PBXGroup; 83 | children = ( 84 | 78F86A521EE0225C0016F81E /* main.m */, 85 | ); 86 | name = "Supporting Files"; 87 | sourceTree = ""; 88 | }; 89 | 78F86A681EE023D40016F81E /* ZCircleSliderDemo */ = { 90 | isa = PBXGroup; 91 | children = ( 92 | 78F86A691EE023F20016F81E /* MainTableViewController.h */, 93 | 78F86A6A1EE023F20016F81E /* MainTableViewController.m */, 94 | 78F86A701EE026E70016F81E /* FirstViewController.h */, 95 | 78F86A711EE026E70016F81E /* FirstViewController.m */, 96 | 78F86A731EE026F30016F81E /* SecondViewController.h */, 97 | 78F86A741EE026F30016F81E /* SecondViewController.m */, 98 | 78F86A541EE0225C0016F81E /* AppDelegate.h */, 99 | 78F86A551EE0225C0016F81E /* AppDelegate.m */, 100 | 78F86A571EE0225C0016F81E /* ViewController.h */, 101 | 78F86A581EE0225C0016F81E /* ViewController.m */, 102 | 78F86A5A1EE0225C0016F81E /* Main.storyboard */, 103 | 78F86A5D1EE0225C0016F81E /* Assets.xcassets */, 104 | 78F86A5F1EE0225C0016F81E /* LaunchScreen.storyboard */, 105 | 78F86A621EE0225C0016F81E /* Info.plist */, 106 | 78F86A761EE027E10016F81E /* DefaultDefine.h */, 107 | ); 108 | name = ZCircleSliderDemo; 109 | sourceTree = ""; 110 | }; 111 | 78F86A6C1EE026CD0016F81E /* ZCircleSlider */ = { 112 | isa = PBXGroup; 113 | children = ( 114 | 78F86A6D1EE026CD0016F81E /* ZCircleSlider.h */, 115 | 78F86A6E1EE026CD0016F81E /* ZCircleSlider.m */, 116 | ); 117 | path = ZCircleSlider; 118 | sourceTree = ""; 119 | }; 120 | /* End PBXGroup section */ 121 | 122 | /* Begin PBXNativeTarget section */ 123 | 78F86A4D1EE0225C0016F81E /* ZCircleSliderDemo */ = { 124 | isa = PBXNativeTarget; 125 | buildConfigurationList = 78F86A651EE0225C0016F81E /* Build configuration list for PBXNativeTarget "ZCircleSliderDemo" */; 126 | buildPhases = ( 127 | 78F86A4A1EE0225C0016F81E /* Sources */, 128 | 78F86A4B1EE0225C0016F81E /* Frameworks */, 129 | 78F86A4C1EE0225C0016F81E /* Resources */, 130 | ); 131 | buildRules = ( 132 | ); 133 | dependencies = ( 134 | ); 135 | name = ZCircleSliderDemo; 136 | productName = ZCircleSliderDemo; 137 | productReference = 78F86A4E1EE0225C0016F81E /* ZCircleSliderDemo.app */; 138 | productType = "com.apple.product-type.application"; 139 | }; 140 | /* End PBXNativeTarget section */ 141 | 142 | /* Begin PBXProject section */ 143 | 78F86A461EE0225C0016F81E /* Project object */ = { 144 | isa = PBXProject; 145 | attributes = { 146 | LastUpgradeCheck = 0830; 147 | ORGANIZATIONNAME = Jixin; 148 | TargetAttributes = { 149 | 78F86A4D1EE0225C0016F81E = { 150 | CreatedOnToolsVersion = 8.3.2; 151 | DevelopmentTeam = 4RTMSXL9KV; 152 | ProvisioningStyle = Automatic; 153 | }; 154 | }; 155 | }; 156 | buildConfigurationList = 78F86A491EE0225C0016F81E /* Build configuration list for PBXProject "ZCircleSliderDemo" */; 157 | compatibilityVersion = "Xcode 3.2"; 158 | developmentRegion = English; 159 | hasScannedForEncodings = 0; 160 | knownRegions = ( 161 | en, 162 | Base, 163 | ); 164 | mainGroup = 78F86A451EE0225C0016F81E; 165 | productRefGroup = 78F86A4F1EE0225C0016F81E /* Products */; 166 | projectDirPath = ""; 167 | projectRoot = ""; 168 | targets = ( 169 | 78F86A4D1EE0225C0016F81E /* ZCircleSliderDemo */, 170 | ); 171 | }; 172 | /* End PBXProject section */ 173 | 174 | /* Begin PBXResourcesBuildPhase section */ 175 | 78F86A4C1EE0225C0016F81E /* Resources */ = { 176 | isa = PBXResourcesBuildPhase; 177 | buildActionMask = 2147483647; 178 | files = ( 179 | 78F86A611EE0225C0016F81E /* LaunchScreen.storyboard in Resources */, 180 | 78F86A5E1EE0225C0016F81E /* Assets.xcassets in Resources */, 181 | 78F86A5C1EE0225C0016F81E /* Main.storyboard in Resources */, 182 | ); 183 | runOnlyForDeploymentPostprocessing = 0; 184 | }; 185 | /* End PBXResourcesBuildPhase section */ 186 | 187 | /* Begin PBXSourcesBuildPhase section */ 188 | 78F86A4A1EE0225C0016F81E /* Sources */ = { 189 | isa = PBXSourcesBuildPhase; 190 | buildActionMask = 2147483647; 191 | files = ( 192 | 78F86A6B1EE023F20016F81E /* MainTableViewController.m in Sources */, 193 | 78F86A721EE026E70016F81E /* FirstViewController.m in Sources */, 194 | 78F86A591EE0225C0016F81E /* ViewController.m in Sources */, 195 | 78F86A751EE026F30016F81E /* SecondViewController.m in Sources */, 196 | 78F86A6F1EE026CD0016F81E /* ZCircleSlider.m in Sources */, 197 | 78F86A561EE0225C0016F81E /* AppDelegate.m in Sources */, 198 | 78F86A531EE0225C0016F81E /* main.m in Sources */, 199 | ); 200 | runOnlyForDeploymentPostprocessing = 0; 201 | }; 202 | /* End PBXSourcesBuildPhase section */ 203 | 204 | /* Begin PBXVariantGroup section */ 205 | 78F86A5A1EE0225C0016F81E /* Main.storyboard */ = { 206 | isa = PBXVariantGroup; 207 | children = ( 208 | 78F86A5B1EE0225C0016F81E /* Base */, 209 | ); 210 | name = Main.storyboard; 211 | sourceTree = ""; 212 | }; 213 | 78F86A5F1EE0225C0016F81E /* LaunchScreen.storyboard */ = { 214 | isa = PBXVariantGroup; 215 | children = ( 216 | 78F86A601EE0225C0016F81E /* Base */, 217 | ); 218 | name = LaunchScreen.storyboard; 219 | sourceTree = ""; 220 | }; 221 | /* End PBXVariantGroup section */ 222 | 223 | /* Begin XCBuildConfiguration section */ 224 | 78F86A631EE0225C0016F81E /* Debug */ = { 225 | isa = XCBuildConfiguration; 226 | buildSettings = { 227 | ALWAYS_SEARCH_USER_PATHS = NO; 228 | CLANG_ANALYZER_NONNULL = YES; 229 | CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE; 230 | CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x"; 231 | CLANG_CXX_LIBRARY = "libc++"; 232 | CLANG_ENABLE_MODULES = YES; 233 | CLANG_ENABLE_OBJC_ARC = YES; 234 | CLANG_WARN_BOOL_CONVERSION = YES; 235 | CLANG_WARN_CONSTANT_CONVERSION = YES; 236 | CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; 237 | CLANG_WARN_DOCUMENTATION_COMMENTS = YES; 238 | CLANG_WARN_EMPTY_BODY = YES; 239 | CLANG_WARN_ENUM_CONVERSION = YES; 240 | CLANG_WARN_INFINITE_RECURSION = YES; 241 | CLANG_WARN_INT_CONVERSION = YES; 242 | CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; 243 | CLANG_WARN_SUSPICIOUS_MOVE = YES; 244 | CLANG_WARN_UNREACHABLE_CODE = YES; 245 | CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; 246 | "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer"; 247 | COPY_PHASE_STRIP = NO; 248 | DEBUG_INFORMATION_FORMAT = dwarf; 249 | ENABLE_STRICT_OBJC_MSGSEND = YES; 250 | ENABLE_TESTABILITY = YES; 251 | GCC_C_LANGUAGE_STANDARD = gnu99; 252 | GCC_DYNAMIC_NO_PIC = NO; 253 | GCC_NO_COMMON_BLOCKS = YES; 254 | GCC_OPTIMIZATION_LEVEL = 0; 255 | GCC_PREPROCESSOR_DEFINITIONS = ( 256 | "DEBUG=1", 257 | "$(inherited)", 258 | ); 259 | GCC_WARN_64_TO_32_BIT_CONVERSION = YES; 260 | GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; 261 | GCC_WARN_UNDECLARED_SELECTOR = YES; 262 | GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; 263 | GCC_WARN_UNUSED_FUNCTION = YES; 264 | GCC_WARN_UNUSED_VARIABLE = YES; 265 | IPHONEOS_DEPLOYMENT_TARGET = 10.3; 266 | MTL_ENABLE_DEBUG_INFO = YES; 267 | ONLY_ACTIVE_ARCH = YES; 268 | SDKROOT = iphoneos; 269 | }; 270 | name = Debug; 271 | }; 272 | 78F86A641EE0225C0016F81E /* Release */ = { 273 | isa = XCBuildConfiguration; 274 | buildSettings = { 275 | ALWAYS_SEARCH_USER_PATHS = NO; 276 | CLANG_ANALYZER_NONNULL = YES; 277 | CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE; 278 | CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x"; 279 | CLANG_CXX_LIBRARY = "libc++"; 280 | CLANG_ENABLE_MODULES = YES; 281 | CLANG_ENABLE_OBJC_ARC = YES; 282 | CLANG_WARN_BOOL_CONVERSION = YES; 283 | CLANG_WARN_CONSTANT_CONVERSION = YES; 284 | CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; 285 | CLANG_WARN_DOCUMENTATION_COMMENTS = YES; 286 | CLANG_WARN_EMPTY_BODY = YES; 287 | CLANG_WARN_ENUM_CONVERSION = YES; 288 | CLANG_WARN_INFINITE_RECURSION = YES; 289 | CLANG_WARN_INT_CONVERSION = YES; 290 | CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; 291 | CLANG_WARN_SUSPICIOUS_MOVE = YES; 292 | CLANG_WARN_UNREACHABLE_CODE = YES; 293 | CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; 294 | "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer"; 295 | COPY_PHASE_STRIP = NO; 296 | DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym"; 297 | ENABLE_NS_ASSERTIONS = NO; 298 | ENABLE_STRICT_OBJC_MSGSEND = YES; 299 | GCC_C_LANGUAGE_STANDARD = gnu99; 300 | GCC_NO_COMMON_BLOCKS = YES; 301 | GCC_WARN_64_TO_32_BIT_CONVERSION = YES; 302 | GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; 303 | GCC_WARN_UNDECLARED_SELECTOR = YES; 304 | GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; 305 | GCC_WARN_UNUSED_FUNCTION = YES; 306 | GCC_WARN_UNUSED_VARIABLE = YES; 307 | IPHONEOS_DEPLOYMENT_TARGET = 10.3; 308 | MTL_ENABLE_DEBUG_INFO = NO; 309 | SDKROOT = iphoneos; 310 | VALIDATE_PRODUCT = YES; 311 | }; 312 | name = Release; 313 | }; 314 | 78F86A661EE0225C0016F81E /* Debug */ = { 315 | isa = XCBuildConfiguration; 316 | buildSettings = { 317 | ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; 318 | DEVELOPMENT_TEAM = 4RTMSXL9KV; 319 | INFOPLIST_FILE = ZCircleSliderDemo/Info.plist; 320 | IPHONEOS_DEPLOYMENT_TARGET = 8.0; 321 | LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks"; 322 | PRODUCT_BUNDLE_IDENTIFIER = Jixin.com.ZCircleSliderDemo; 323 | PRODUCT_NAME = "$(TARGET_NAME)"; 324 | }; 325 | name = Debug; 326 | }; 327 | 78F86A671EE0225C0016F81E /* Release */ = { 328 | isa = XCBuildConfiguration; 329 | buildSettings = { 330 | ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; 331 | DEVELOPMENT_TEAM = 4RTMSXL9KV; 332 | INFOPLIST_FILE = ZCircleSliderDemo/Info.plist; 333 | IPHONEOS_DEPLOYMENT_TARGET = 8.0; 334 | LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks"; 335 | PRODUCT_BUNDLE_IDENTIFIER = Jixin.com.ZCircleSliderDemo; 336 | PRODUCT_NAME = "$(TARGET_NAME)"; 337 | }; 338 | name = Release; 339 | }; 340 | /* End XCBuildConfiguration section */ 341 | 342 | /* Begin XCConfigurationList section */ 343 | 78F86A491EE0225C0016F81E /* Build configuration list for PBXProject "ZCircleSliderDemo" */ = { 344 | isa = XCConfigurationList; 345 | buildConfigurations = ( 346 | 78F86A631EE0225C0016F81E /* Debug */, 347 | 78F86A641EE0225C0016F81E /* Release */, 348 | ); 349 | defaultConfigurationIsVisible = 0; 350 | defaultConfigurationName = Release; 351 | }; 352 | 78F86A651EE0225C0016F81E /* Build configuration list for PBXNativeTarget "ZCircleSliderDemo" */ = { 353 | isa = XCConfigurationList; 354 | buildConfigurations = ( 355 | 78F86A661EE0225C0016F81E /* Debug */, 356 | 78F86A671EE0225C0016F81E /* Release */, 357 | ); 358 | defaultConfigurationIsVisible = 0; 359 | defaultConfigurationName = Release; 360 | }; 361 | /* End XCConfigurationList section */ 362 | }; 363 | rootObject = 78F86A461EE0225C0016F81E /* Project object */; 364 | } 365 | --------------------------------------------------------------------------------