├── 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 | 
7 | * 有如下的特点:
8 | * 滑动的响应区域为圆环上,并且靠近滑块;
9 | * 点击滑块时,滑块有一个放大的动画,手指离开屏幕时滑块恢复原来大小;
10 | * 当value=0%时,滑块不可以再逆时针滑动,当value=100%时,滑块不可以再顺时针滑动。
11 | * 最后我自己再加了一个设计,指示音频loading的进度(这样以来圆环就有了三层)。
12 |
13 |
14 | ## 二、成果展示Demo
15 | * 重复拖动
16 |
17 | 
18 |
19 | * 限定360度
20 |
21 | 
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 |
--------------------------------------------------------------------------------