RQSwitch.m 26 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696
  1. //
  2. // RQSwitch.m
  3. // jiaPei
  4. //
  5. // Created by 张嵘 on 2022/4/27.
  6. // Copyright © 2022 JCZ. All rights reserved.
  7. //
  8. #import "RQSwitch.h"
  9. #import <QuartzCore/QuartzCore.h>
  10. @interface RQSwitch()
  11. @end
  12. @implementation RQSwitch {
  13. float thumbOnPosition;
  14. float thumbOffPosition;
  15. float bounceOffset;
  16. CAShapeLayer *rippleLayer;
  17. }
  18. - (id)init {
  19. self = [self initWithSize:RQSwitchSizeNormal
  20. style:RQSwitchStyleDefault
  21. state:RQSwitchStateOn];
  22. return self;
  23. }
  24. - (id)initWithSize:(RQSwitchSize)size state:(RQSwitchState)state {
  25. @weakify(self)
  26. self.isEnabled = YES;
  27. self.isRippleEnabled = YES;
  28. self.isBounceEnabled = YES;
  29. bounceOffset = 3.0f;
  30. CGRect frame;
  31. CGRect trackFrame = CGRectZero;
  32. CGRect thumbFrame = CGRectZero;
  33. switch (size) {
  34. case RQSwitchSizeBig:
  35. frame = CGRectMake(0, 0, 50, 40);
  36. self.trackSize = CGSizeMake(frame.size.width, 23.0);
  37. self.thumbSize = CGSizeMake(frame.size.width / 2.f, 31.0);
  38. break;
  39. case RQSwitchSizeNormal:
  40. frame = CGRectMake(0, 0, 105, 25);
  41. self.trackSize = CGSizeMake(frame.size.width, 25.0);
  42. self.thumbSize = CGSizeMake(frame.size.width / 2.f, 25.0);
  43. break;
  44. case RQSwitchSizeSmall:
  45. frame = CGRectMake(0, 0, 30, 25);
  46. self.trackSize = CGSizeMake(frame.size.width, 13.0);
  47. self.thumbSize = CGSizeMake(frame.size.width / 2.f, 18.0);
  48. break;
  49. case RQSwitchSizeSubject:
  50. frame = CGRectMake(0, 0, RQ_FIT_HORIZONTAL(105.f), RQ_FIT_HORIZONTAL(25.f));
  51. self.trackSize = CGSizeMake(frame.size.width, frame.size.height);
  52. self.thumbSize = CGSizeMake(frame.size.width / 2.f, frame.size.height);
  53. break;
  54. default:
  55. frame = CGRectMake(0, 0, 40, 30);
  56. self.trackSize = CGSizeMake(frame.size.width, 13.0);
  57. self.thumbSize = CGSizeMake(frame.size.width / 2.f, 20.0);
  58. break;
  59. }
  60. trackFrame.size = self.trackSize;
  61. trackFrame.origin.x = 0.0;
  62. trackFrame.origin.y = (frame.size.height - trackFrame.size.height) / 2;
  63. thumbFrame.size = self.thumbSize;
  64. thumbFrame.origin.x = 0.0;
  65. thumbFrame.origin.y = (frame.size.height - thumbFrame.size.height) / 2;
  66. // Actual initialization with selected size
  67. self = [super initWithFrame:frame];
  68. self.track = [[UIView alloc] initWithFrame:trackFrame];
  69. self.track.backgroundColor = [UIColor grayColor];
  70. [self.track addSubview:self.trackLeftLabel];
  71. self.track.layer.borderWidth = 1.0f;
  72. self.track.layer.borderColor = RQColorFromHexString(@"#B8C0CC").CGColor;
  73. [self.trackLeftLabel mas_makeConstraints:^(MASConstraintMaker *make) {
  74. @strongify(self)
  75. make.left.mas_equalTo(self.track);
  76. make.centerY.mas_equalTo(self.track);
  77. make.size.mas_equalTo(self.thumbSize);
  78. }];
  79. [self.track addSubview:self.trackRightLabel];
  80. [self.trackRightLabel mas_makeConstraints:^(MASConstraintMaker *make) {
  81. @strongify(self)
  82. make.right.mas_equalTo(self.track);
  83. make.centerY.mas_equalTo(self.track);
  84. make.size.mas_equalTo(self.thumbSize);
  85. }];
  86. self.track.layer.cornerRadius = MIN(self.track.frame.size.height, self.track.frame.size.width)/2;
  87. [self addSubview:self.track];
  88. self.switchThumb = [[UIButton alloc] initWithFrame:thumbFrame];
  89. [self.switchThumb setTitleColor:UIColor.whiteColor forState:UIControlStateNormal];
  90. self.switchThumb.backgroundColor = [UIColor whiteColor];
  91. self.switchThumb.layer.cornerRadius = self.switchThumb.frame.size.height/2;
  92. self.switchThumb.layer.shadowOpacity = 0.5;
  93. self.switchThumb.layer.shadowOffset = CGSizeMake(0.0, 1.0);
  94. self.switchThumb.layer.shadowColor = [UIColor blackColor].CGColor;
  95. self.switchThumb.layer.shadowRadius = 2.0f;
  96. // Add events for user action
  97. [self.switchThumb addTarget:self action:@selector(onTouchDown:withEvent:) forControlEvents:UIControlEventTouchDown];
  98. [self.switchThumb addTarget:self action:@selector(onTouchUpOutsideOrCanceled:withEvent:) forControlEvents:UIControlEventTouchUpOutside];
  99. [self.switchThumb addTarget:self action:@selector(switchThumbTapped:) forControlEvents:UIControlEventTouchUpInside];
  100. [self.switchThumb addTarget:self action:@selector(onTouchDragInside:withEvent:) forControlEvents:UIControlEventTouchDragInside];
  101. [self.switchThumb addTarget:self action:@selector(onTouchUpOutsideOrCanceled:withEvent:) forControlEvents:UIControlEventTouchCancel];
  102. [self addSubview:self.switchThumb];
  103. thumbOnPosition = self.frame.size.width - self.switchThumb.frame.size.width;
  104. thumbOffPosition = self.switchThumb.frame.origin.x;
  105. // Set thumb's initial position from state property
  106. switch (state) {
  107. case RQSwitchStateOn:
  108. self.isOn = YES;
  109. self.switchThumb.backgroundColor = self.thumbOnTintColor;
  110. CGRect thumbFrame = self.switchThumb.frame;
  111. thumbFrame.origin.x = thumbOnPosition;
  112. self.switchThumb.frame = thumbFrame;
  113. break;
  114. case RQSwitchStateOff:
  115. self.isOn = NO;
  116. self.switchThumb.backgroundColor = self.thumbOffTintColor;
  117. break;
  118. default:
  119. self.isOn = NO;
  120. self.switchThumb.backgroundColor = self.thumbOffTintColor;
  121. break;
  122. }
  123. UITapGestureRecognizer *singleTap =
  124. [[UITapGestureRecognizer alloc] initWithTarget:self
  125. action:@selector(switchAreaTapped:)];
  126. [self addGestureRecognizer:singleTap];
  127. self.isOn = NO;
  128. [self.switchThumb setTitleNormal:@"科一"];
  129. return self;
  130. }
  131. - (id)initWithSize:(RQSwitchSize)size style:(RQSwitchStyle)style state:(RQSwitchState)state {
  132. switch (style) {
  133. case RQSwitchStyleLight:
  134. self.thumbOnTintColor = [UIColor colorWithRed:0./255. green:134./255. blue:117./255. alpha:1.0];
  135. self.thumbOffTintColor = [UIColor colorWithRed:237./255. green:237./255. blue:237./255. alpha:1.0];
  136. self.trackOnTintColor = [UIColor colorWithRed:90./255. green:178./255. blue:169./255. alpha:1.0];
  137. self.trackOffTintColor = [UIColor colorWithRed:129./255. green:129./255. blue:129./255. alpha:1.0];
  138. self.thumbDisabledTintColor = [UIColor colorWithRed:175./255. green:175./255. blue:175./255. alpha:1.0];
  139. self.trackDisabledTintColor = [UIColor colorWithRed:203./255. green:203./255. blue:203./255. alpha:1.0];
  140. self.rippleFillColor = [UIColor grayColor];
  141. break;
  142. case RQSwitchStyleDark:
  143. self.thumbOnTintColor = [UIColor colorWithRed:109./255. green:194./255. blue:184./255. alpha:1.0];
  144. self.thumbOffTintColor = [UIColor colorWithRed:175./255. green:175./255. blue:175./255. alpha:1.0];
  145. self.trackOnTintColor = [UIColor colorWithRed:72./255. green:109./255. blue:105./255. alpha:1.0];
  146. self.trackOffTintColor = [UIColor colorWithRed:94./255. green:94./255. blue:94./255. alpha:1.0];
  147. self.thumbDisabledTintColor = [UIColor colorWithRed:50./255. green:51./255. blue:50./255. alpha:1.0];
  148. self.trackDisabledTintColor = [UIColor colorWithRed:56./255. green:56./255. blue:56./255. alpha:1.0];
  149. self.rippleFillColor = [UIColor grayColor];
  150. break;
  151. case RQSwitchStyleSubject:
  152. self.thumbOnTintColor = RQColorFromHexString(@"#498EF5");
  153. self.thumbOffTintColor = RQColorFromHexString(@"#498EF5");
  154. self.trackOnTintColor = UIColor.whiteColor;
  155. self.trackOffTintColor = UIColor.whiteColor;
  156. self.thumbDisabledTintColor = RQColorFromHexString(@"#498EF5");
  157. self.trackDisabledTintColor = UIColor.whiteColor;
  158. self.rippleFillColor = [UIColor blueColor];
  159. break;
  160. default:
  161. self.thumbOnTintColor = [UIColor colorWithRed:52./255. green:109./255. blue:241./255. alpha:1.0];
  162. self.thumbOffTintColor = [UIColor colorWithRed:249./255. green:249./255. blue:249./255. alpha:1.0];
  163. self.trackOnTintColor = [UIColor colorWithRed:143./255. green:179./255. blue:247./255. alpha:1.0];
  164. self.trackOffTintColor = [UIColor colorWithRed:193./255. green:193./255. blue:193./255. alpha:1.0];
  165. self.thumbDisabledTintColor = [UIColor colorWithRed:174./255. green:174./255. blue:174./255. alpha:1.0];
  166. self.trackDisabledTintColor = [UIColor colorWithRed:203./255. green:203./255. blue:203./255. alpha:1.0];
  167. self.rippleFillColor = [UIColor blueColor];
  168. break;
  169. }
  170. self = [self initWithSize:size state:state];
  171. return self;
  172. }
  173. // When addSubview is called
  174. - (void)willMoveToSuperview:(UIView *)newSuperview {
  175. [super willMoveToSuperview:newSuperview];
  176. // Set colors for proper positions
  177. if(self.isOn == YES) {
  178. self.switchThumb.backgroundColor = self.thumbOnTintColor;
  179. self.track.backgroundColor = self.trackOnTintColor;
  180. } else {
  181. self.switchThumb.backgroundColor = self.thumbOffTintColor;
  182. self.track.backgroundColor = self.trackOffTintColor;
  183. // set initial position
  184. [self changeThumbStateOFFwithoutAnimation];
  185. }
  186. if (self.isEnabled == NO) {
  187. self.switchThumb.backgroundColor = self.thumbDisabledTintColor;
  188. self.track.backgroundColor = self.trackDisabledTintColor;
  189. }
  190. // Set bounce value, 3.0 if enabled and none for disabled
  191. if (self.isBounceEnabled == YES) {
  192. bounceOffset = 3.0f;
  193. } else {
  194. bounceOffset = 0.0f;
  195. }
  196. }
  197. // Just returns current switch state,
  198. - (BOOL)getSwitchState {
  199. return self.isOn;
  200. }
  201. // Change switch state if necessary, with the animated option parameter
  202. - (void)setOn:(BOOL)on animated:(BOOL)animated {
  203. if (on == YES) {
  204. if (animated == YES) {
  205. // set on with animation
  206. [self changeThumbStateONwithAnimation];
  207. } else {
  208. // set on without animation
  209. [self changeThumbStateONwithoutAnimation];
  210. }
  211. } else {
  212. if (animated == YES) {
  213. // set off with animation
  214. [self changeThumbStateOFFwithAnimation];
  215. } else {
  216. // set off without animation
  217. [self changeThumbStateOFFwithoutAnimation];
  218. }
  219. }
  220. }
  221. // Override setEnabled: function for changing color to the correct style
  222. - (void)setEnabled:(BOOL)enabled {
  223. [super setEnabled:enabled];
  224. // Animation for better transfer, better UX
  225. [UIView animateWithDuration:0.1 animations:^{
  226. if (enabled == YES) {
  227. if (self.isOn == YES) {
  228. self.switchThumb.backgroundColor = self.thumbOnTintColor;
  229. self.track.backgroundColor = self.trackOnTintColor;
  230. } else {
  231. self.switchThumb.backgroundColor = self.thumbOffTintColor;
  232. self.track.backgroundColor = self.trackOffTintColor;
  233. }
  234. self.isEnabled = YES;
  235. } else {
  236. // if disabled
  237. self.switchThumb.backgroundColor = self.thumbDisabledTintColor;
  238. self.track.backgroundColor = self.trackDisabledTintColor;
  239. self.isEnabled = NO;
  240. }
  241. }];
  242. }
  243. - (void)setTrackStr:(NSString *)trackStr {
  244. _trackStr = trackStr;
  245. }
  246. - (void)setThumbStr:(NSString *)thumbStr {
  247. _thumbStr = thumbStr;
  248. [self.switchThumb setTitleNormal:thumbStr];
  249. }
  250. //The event handling method
  251. - (void)switchAreaTapped:(UITapGestureRecognizer *)recognizer {
  252. // Delegate method
  253. if ([self.delegate respondsToSelector:@selector(switchStateChanged:)]) {
  254. if (self.isOn == YES) {
  255. [self.delegate switchStateChanged:RQSwitchStateOff];
  256. } else{
  257. [self.delegate switchStateChanged:RQSwitchStateOn];
  258. }
  259. }
  260. [self changeThumbState];
  261. }
  262. - (void)changeThumbState {
  263. // NSLog(@"thumb origin pos: %@", NSStringFromCGRect(self.switchThumb.frame));
  264. if (self.isOn == YES) {
  265. [self.switchThumb setTitleNormal:@"科一"];
  266. [self changeThumbStateOFFwithAnimation];
  267. } else {
  268. [self.switchThumb setTitleNormal:@"科四"];
  269. [self changeThumbStateONwithAnimation];
  270. }
  271. if (self.isRippleEnabled == YES) {
  272. [self animateRippleEffect];
  273. }
  274. }
  275. - (void)changeThumbStateONwithAnimation {
  276. // switch movement animation
  277. [UIView animateWithDuration:0.15f
  278. delay:0.05f
  279. options:UIViewAnimationOptionCurveEaseInOut
  280. animations:^{
  281. CGRect thumbFrame = self.switchThumb.frame;
  282. thumbFrame.origin.x = thumbOnPosition+bounceOffset;
  283. self.switchThumb.frame = thumbFrame;
  284. if (self.isEnabled == YES) {
  285. self.switchThumb.backgroundColor = self.thumbOnTintColor;
  286. self.track.backgroundColor = self.trackOnTintColor;
  287. } else {
  288. self.switchThumb.backgroundColor = self.thumbDisabledTintColor;
  289. self.track.backgroundColor = self.trackDisabledTintColor;
  290. }
  291. self.userInteractionEnabled = NO;
  292. self.trackLeftLabel.alpha = 1;
  293. self.trackRightLabel.alpha = 0;
  294. } completion:^(BOOL finished) {
  295. // change state to ON
  296. if (self.isOn == NO) {
  297. self.isOn = YES; // Expressly put this code here to change surely and send action correctly
  298. [self sendActionsForControlEvents:UIControlEventValueChanged];
  299. }
  300. self.isOn = YES;
  301. // NSLog(@"now isOn: %d", self.isOn);
  302. // NSLog(@"thumb end pos: %@", NSStringFromCGRect(self.switchThumb.frame));
  303. // Bouncing effect: Move thumb a bit, for better UX
  304. [UIView animateWithDuration:0.15f
  305. animations:^{
  306. // Bounce to the position
  307. CGRect thumbFrame = self.switchThumb.frame;
  308. thumbFrame.origin.x = thumbOnPosition;
  309. self.switchThumb.frame = thumbFrame;
  310. } completion:^(BOOL finished){
  311. self.userInteractionEnabled = YES;
  312. }];
  313. }];
  314. }
  315. - (void)changeThumbStateOFFwithAnimation {
  316. // switch movement animation
  317. [UIView animateWithDuration:0.15f
  318. delay:0.05f
  319. options:UIViewAnimationOptionCurveEaseInOut
  320. animations:^{
  321. CGRect thumbFrame = self.switchThumb.frame;
  322. thumbFrame.origin.x = thumbOffPosition-bounceOffset;
  323. self.switchThumb.frame = thumbFrame;
  324. if (self.isEnabled == YES) {
  325. self.switchThumb.backgroundColor = self.thumbOffTintColor;
  326. self.track.backgroundColor = self.trackOffTintColor;
  327. }
  328. else {
  329. self.switchThumb.backgroundColor = self.thumbDisabledTintColor;
  330. self.track.backgroundColor = self.trackDisabledTintColor;
  331. }
  332. self.userInteractionEnabled = NO;
  333. self.trackLeftLabel.alpha = 0;
  334. self.trackRightLabel.alpha = 1;
  335. } completion:^(BOOL finished) {
  336. // change state to OFF
  337. if (self.isOn == YES) {
  338. self.isOn = NO; // Expressly put this code here to change surely and send action correctly
  339. [self sendActionsForControlEvents:UIControlEventValueChanged];
  340. }
  341. self.isOn = NO;
  342. // NSLog(@"now isOn: %d", self.isOn);
  343. // NSLog(@"thumb end pos: %@", NSStringFromCGRect(self.switchThumb.frame));
  344. // Bouncing effect: Move thumb a bit, for better UX
  345. [UIView animateWithDuration:0.15f
  346. animations:^ {
  347. // Bounce to the position
  348. CGRect thumbFrame = self.switchThumb.frame;
  349. thumbFrame.origin.x = thumbOffPosition;
  350. self.switchThumb.frame = thumbFrame;
  351. } completion:^(BOOL finished) {
  352. self.userInteractionEnabled = YES;
  353. }];
  354. }];
  355. }
  356. // Without animation
  357. - (void)changeThumbStateONwithoutAnimation {
  358. CGRect thumbFrame = self.switchThumb.frame;
  359. thumbFrame.origin.x = thumbOnPosition;
  360. self.switchThumb.frame = thumbFrame;
  361. if (self.isEnabled == YES) {
  362. self.switchThumb.backgroundColor = self.thumbOnTintColor;
  363. self.track.backgroundColor = self.trackOnTintColor;
  364. } else {
  365. self.switchThumb.backgroundColor = self.thumbDisabledTintColor;
  366. self.track.backgroundColor = self.trackDisabledTintColor;
  367. }
  368. if (self.isOn == NO) {
  369. self.isOn = YES;
  370. [self sendActionsForControlEvents:UIControlEventValueChanged];
  371. }
  372. self.isOn = YES;
  373. }
  374. // Without animation
  375. - (void)changeThumbStateOFFwithoutAnimation {
  376. CGRect thumbFrame = self.switchThumb.frame;
  377. thumbFrame.origin.x = thumbOffPosition;
  378. self.switchThumb.frame = thumbFrame;
  379. if (self.isEnabled == YES) {
  380. self.switchThumb.backgroundColor = self.thumbOffTintColor;
  381. self.track.backgroundColor = self.trackOffTintColor;
  382. } else {
  383. self.switchThumb.backgroundColor = self.thumbDisabledTintColor;
  384. self.track.backgroundColor = self.trackDisabledTintColor;
  385. }
  386. if (self.isOn == YES) {
  387. self.isOn = NO;
  388. [self sendActionsForControlEvents:UIControlEventValueChanged];
  389. }
  390. self.isOn = NO;
  391. }
  392. // Initialize and appear ripple effect
  393. - (void)initializeRipple {
  394. // Ripple size is twice as large as switch thumb
  395. float rippleScale = 2;
  396. CGRect rippleFrame = CGRectZero;
  397. rippleFrame.origin.x = -self.switchThumb.frame.size.width/(rippleScale * 2);
  398. rippleFrame.origin.y = -self.switchThumb.frame.size.height/(rippleScale * 2);
  399. rippleFrame.size.height = self.switchThumb.frame.size.height * rippleScale;
  400. rippleFrame.size.width = rippleFrame.size.height;
  401. // NSLog(@"");
  402. // NSLog(@"thumb State: %d", self.isOn);
  403. // NSLog(@"switchThumb pos: %@", NSStringFromCGRect(self.switchThumb.frame));
  404. UIBezierPath *path = [UIBezierPath bezierPathWithRoundedRect:rippleFrame cornerRadius:self.switchThumb.layer.cornerRadius*2];
  405. // Set ripple layer attributes
  406. rippleLayer = [CAShapeLayer layer];
  407. rippleLayer.path = path.CGPath;
  408. rippleLayer.frame = rippleFrame;
  409. rippleLayer.opacity = 0.2;
  410. rippleLayer.strokeColor = [UIColor clearColor].CGColor;
  411. rippleLayer.fillColor = self.rippleFillColor.CGColor;
  412. rippleLayer.lineWidth = 0;
  413. // NSLog(@"Ripple origin pos: %@", NSStringFromCGRect(circleShape.frame));
  414. [self.switchThumb.layer insertSublayer:rippleLayer below:self.switchThumb.layer];
  415. // [self.layer insertSublayer:circleShape above:self.switchThumb.layer];
  416. }
  417. - (void)animateRippleEffect {
  418. // Create ripple layer
  419. if ( rippleLayer == nil) {
  420. [self initializeRipple];
  421. }
  422. // Animation begins from here
  423. rippleLayer.opacity = 0.0;
  424. [CATransaction begin];
  425. //remove layer after animation completed
  426. [CATransaction setCompletionBlock:^{
  427. [rippleLayer removeFromSuperlayer];
  428. rippleLayer = nil;
  429. }];
  430. // Scale ripple to the modelate size
  431. CABasicAnimation *scaleAnimation = [CABasicAnimation animationWithKeyPath:@"transform.scale"];
  432. scaleAnimation.fromValue = [NSNumber numberWithFloat:0.5];
  433. scaleAnimation.toValue = [NSNumber numberWithFloat:1.25];
  434. // Alpha animation for smooth disappearing
  435. CABasicAnimation *alphaAnimation = [CABasicAnimation animationWithKeyPath:@"opacity"];
  436. alphaAnimation.fromValue = @0.4;
  437. alphaAnimation.toValue = @0;
  438. // Do above animations at the same time with proper duration
  439. CAAnimationGroup *animation = [CAAnimationGroup animation];
  440. animation.animations = @[scaleAnimation, alphaAnimation];
  441. animation.duration = 0.4f;
  442. animation.timingFunction = [CAMediaTimingFunction functionWithName:kCAMediaTimingFunctionEaseOut];
  443. [rippleLayer addAnimation:animation forKey:nil];
  444. [CATransaction commit];
  445. // End of animation, then remove ripple layer
  446. // NSLog(@"Ripple removed");
  447. }
  448. - (void)onTouchDown:(UIButton*)btn withEvent:(UIEvent*)event {
  449. // NSLog(@"touchDown called");
  450. if (self.isRippleEnabled == YES) {
  451. [self initializeRipple];
  452. }
  453. // Animate for appearing ripple circle when tap and hold the switch thumb
  454. [CATransaction begin];
  455. CABasicAnimation *scaleAnimation = [CABasicAnimation animationWithKeyPath:@"transform.scale"];
  456. scaleAnimation.fromValue = [NSNumber numberWithFloat:0.0];
  457. scaleAnimation.toValue = [NSNumber numberWithFloat:1.0];
  458. CABasicAnimation *alphaAnimation = [CABasicAnimation animationWithKeyPath:@"opacity"];
  459. alphaAnimation.fromValue = @0;
  460. alphaAnimation.toValue = @0.2;
  461. // Do above animations at the same time with proper duration
  462. CAAnimationGroup *animation = [CAAnimationGroup animation];
  463. animation.animations = @[scaleAnimation, alphaAnimation];
  464. animation.duration = 0.4f;
  465. animation.timingFunction = [CAMediaTimingFunction functionWithName:kCAMediaTimingFunctionEaseOut];
  466. [rippleLayer addAnimation:animation forKey:nil];
  467. [CATransaction commit];
  468. // NSLog(@"Ripple end pos: %@", NSStringFromCGRect(circleShape.frame));
  469. }
  470. // Change thumb state when touchUPInside action is detected
  471. - (void)switchThumbTapped: (id)sender {
  472. // NSLog(@"touch up inside");
  473. // NSLog(@"track midPosX: %f", CGRectGetMidX(self.track.frame));
  474. // NSLog(@"%@", NSStringFromCGRect(self.switchThumb.frame));
  475. // Delegate method
  476. if ([self.delegate respondsToSelector:@selector(switchStateChanged:)]) {
  477. if (self.isOn == YES) {
  478. [self.delegate switchStateChanged:RQSwitchStateOff];
  479. } else {
  480. [self.delegate switchStateChanged:RQSwitchStateOn];
  481. }
  482. }
  483. [self changeThumbState];
  484. }
  485. // Change thumb state when touchUPOutside action is detected
  486. - (void)onTouchUpOutsideOrCanceled:(UIButton*)btn withEvent:(UIEvent*)event {
  487. // NSLog(@"Touch released at ouside");
  488. UITouch *touch = [[event touchesForView:btn] anyObject];
  489. CGPoint prevPos = [touch previousLocationInView:btn];
  490. CGPoint pos = [touch locationInView:btn];
  491. float dX = pos.x-prevPos.x;
  492. //Get the new origin after this motion
  493. float newXOrigin = btn.frame.origin.x + dX;
  494. //NSLog(@"Released tap X pos: %f", newXOrigin);
  495. if (newXOrigin > (self.frame.size.width - self.switchThumb.frame.size.width)/2) {
  496. //NSLog(@"thumb pos should be set *ON*");
  497. [self changeThumbStateONwithAnimation];
  498. } else {
  499. //NSLog(@"thumb pos should be set *OFF*");
  500. [self changeThumbStateOFFwithAnimation];
  501. }
  502. if (self.isRippleEnabled == YES) {
  503. [self animateRippleEffect];
  504. }
  505. }
  506. // Drag the switch thumb
  507. - (void)onTouchDragInside:(UIButton*)btn withEvent:(UIEvent*)event {
  508. //This code can go awry if there is more than one finger on the screen
  509. UITouch *touch = [[event touchesForView:btn] anyObject];
  510. CGPoint prevPos = [touch previousLocationInView:btn];
  511. CGPoint pos = [touch locationInView:btn];
  512. float dX = pos.x-prevPos.x;
  513. //Get the original position of the thumb
  514. CGRect thumbFrame = btn.frame;
  515. thumbFrame.origin.x += dX;
  516. //Make sure it's within two bounds
  517. thumbFrame.origin.x = MIN(thumbFrame.origin.x,thumbOnPosition);
  518. thumbFrame.origin.x = MAX(thumbFrame.origin.x,thumbOffPosition);
  519. //Set the thumb's new frame if need to
  520. if(thumbFrame.origin.x != btn.frame.origin.x) {
  521. btn.frame = thumbFrame;
  522. }
  523. }
  524. #pragma mark - LazyLoad
  525. - (UIColor *)thumbOnTintColor {
  526. if (!_thumbOnTintColor) {
  527. _thumbOnTintColor = [UIColor colorWithRed:52./255. green:109./255. blue:241./255. alpha:1.0];
  528. }
  529. return _thumbOnTintColor;
  530. }
  531. - (UIColor *)thumbOffTintColor {
  532. if (!_thumbOffTintColor) {
  533. _thumbOffTintColor = [UIColor colorWithRed:249./255. green:249./255. blue:249./255. alpha:1.0];
  534. }
  535. return _thumbOffTintColor;
  536. }
  537. - (UIColor *)trackOnTintColor {
  538. if (!_trackOnTintColor) {
  539. _trackOnTintColor = [UIColor colorWithRed:143./255. green:179./255. blue:247./255. alpha:1.0];
  540. }
  541. return _trackOnTintColor;
  542. }
  543. - (UIColor *)trackOffTintColor {
  544. if (!_trackOffTintColor) {
  545. _trackOffTintColor = [UIColor colorWithRed:193./255. green:193./255. blue:193./255. alpha:1.0];
  546. }
  547. return _trackOffTintColor;
  548. }
  549. - (UIColor *)thumbDisabledTintColor {
  550. if (!_thumbDisabledTintColor) {
  551. _thumbDisabledTintColor = [UIColor colorWithRed:174./255. green:174./255. blue:174./255. alpha:1.0];
  552. }
  553. return _thumbDisabledTintColor;
  554. }
  555. - (UIColor *)trackDisabledTintColor {
  556. if (!_trackDisabledTintColor) {
  557. _trackDisabledTintColor = [UIColor colorWithRed:203./255. green:203./255. blue:203./255. alpha:1.0];
  558. }
  559. return _trackDisabledTintColor;
  560. }
  561. - (UIColor *)rippleFillColor {
  562. if (!_rippleFillColor) {
  563. _rippleFillColor = [UIColor blueColor];
  564. }
  565. return _rippleFillColor;
  566. }
  567. - (CGSize)trackSize {
  568. if (CGSizeEqualToSize(CGSizeZero, _trackSize)) {
  569. _trackSize = CGSizeMake(58, 25);
  570. }
  571. return _trackSize;
  572. }
  573. - (CGSize)thumbSize {
  574. if (CGSizeEqualToSize(CGSizeZero, _thumbSize)) {
  575. _thumbSize = CGSizeMake(58, 25);
  576. }
  577. return _thumbSize;
  578. }
  579. - (UILabel *)trackLeftLabel {
  580. if (!_trackLeftLabel) {
  581. _trackLeftLabel = [[UILabel alloc] init];
  582. _trackLeftLabel.textAlignment = NSTextAlignmentCenter;
  583. _trackLeftLabel.textColor = RQ_MAIN_TEXT_COLOR_3;
  584. _trackLeftLabel.font = RQRegularFont_15;
  585. _trackLeftLabel.text = @"科一";
  586. }
  587. return _trackLeftLabel;;
  588. }
  589. - (UILabel *)trackRightLabel {
  590. if (!_trackRightLabel) {
  591. _trackRightLabel = [[UILabel alloc] init];
  592. _trackRightLabel.textAlignment = NSTextAlignmentCenter;
  593. _trackRightLabel.textColor = RQ_MAIN_TEXT_COLOR_3;
  594. _trackRightLabel.font = RQRegularFont_15;
  595. _trackRightLabel.text = @"科四";
  596. }
  597. return _trackRightLabel;;
  598. }
  599. @end