1/** 2 * Copyright (c) Facebook, Inc. and its affiliates. 3 * 4 * This source code is licensed under the MIT license found in the 5 * LICENSE file in the root directory of this source tree. 6 */ 7 8#import "RNCSliderManager.h" 9 10#import <React/RCTBridge.h> 11#import <React/RCTEventDispatcher.h> 12#import "RNCSlider.h" 13#import <React/UIView+React.h> 14 15@implementation RNCSliderManager 16{ 17 BOOL _isSliding; 18} 19 20RCT_EXPORT_MODULE() 21 22- (UIView *)view 23{ 24 RNCSlider *slider = [RNCSlider new]; 25 [slider addTarget:self action:@selector(sliderValueChanged:) 26 forControlEvents:UIControlEventValueChanged]; 27 [slider addTarget:self action:@selector(sliderTouchStart:) 28 forControlEvents:UIControlEventTouchDown]; 29 [slider addTarget:self action:@selector(sliderTouchEnd:) 30 forControlEvents:(UIControlEventTouchUpInside | 31 UIControlEventTouchUpOutside | 32 UIControlEventTouchCancel)]; 33 34 UITapGestureRecognizer *tapGesturer; 35 tapGesturer = [[UITapGestureRecognizer alloc] initWithTarget: self action:@selector(tapHandler:)]; 36 [tapGesturer setNumberOfTapsRequired: 1]; 37 [slider addGestureRecognizer:tapGesturer]; 38 39 return slider; 40} 41 42- (void)tapHandler:(UITapGestureRecognizer *)gesture { 43 if ([gesture.view class] != [RNCSlider class]) { 44 return; 45 } 46 RNCSlider *slider = (RNCSlider *)gesture.view; 47 slider.isSliding = _isSliding; 48 49 // Ignore this tap if in the middle of a slide. 50 if (_isSliding) { 51 return; 52 } 53 54 if (!slider.tapToSeek) { 55 return; 56 } 57 58 CGPoint touchPoint = [gesture locationInView:slider]; 59 float rangeWidth = slider.maximumValue - slider.minimumValue; 60 float sliderPercent = touchPoint.x / slider.bounds.size.width; 61 slider.lastValue = slider.value; 62 float value = slider.minimumValue + (rangeWidth * sliderPercent); 63 64 [slider setValue:[slider discreteValue:value] animated: YES]; 65 66 if (slider.onRNCSliderSlidingStart) { 67 slider.onRNCSliderSlidingStart(@{ 68 @"value": @(slider.lastValue), 69 }); 70 } 71 72 // Trigger onValueChange to address https://github.com/react-native-community/react-native-slider/issues/212 73 if (slider.onRNCSliderValueChange) { 74 slider.onRNCSliderValueChange(@{ 75 @"value": @(slider.value), 76 }); 77 } 78 79 if (slider.onRNCSliderSlidingComplete) { 80 slider.onRNCSliderSlidingComplete(@{ 81 @"value": @(slider.value), 82 }); 83 } 84} 85 86static void RNCSendSliderEvent(RNCSlider *sender, BOOL continuous, BOOL isSlidingStart) 87{ 88 float value = [sender discreteValue:sender.value]; 89 90 if (value < sender.lowerLimit) { 91 value = sender.lowerLimit; 92 [sender setValue:value animated:NO]; 93 } else if (value > sender.upperLimit) { 94 value = sender.upperLimit; 95 [sender setValue:value animated:NO]; 96 } 97 98 if(!sender.isSliding) { 99 [sender setValue:value animated:NO]; 100 } 101 102 if (continuous) { 103 if (sender.onRNCSliderValueChange && sender.lastValue != value) { 104 sender.onRNCSliderValueChange(@{ 105 @"value": @(value), 106 }); 107 } 108 } else { 109 if (sender.onRNCSliderSlidingComplete && !isSlidingStart) { 110 sender.onRNCSliderSlidingComplete(@{ 111 @"value": @(value), 112 }); 113 } 114 if (sender.onRNCSliderSlidingStart && isSlidingStart) { 115 sender.onRNCSliderSlidingStart(@{ 116 @"value": @(value), 117 }); 118 } 119 } 120 121 sender.lastValue = value; 122} 123 124- (void)sliderValueChanged:(RNCSlider *)sender 125{ 126 RNCSendSliderEvent(sender, YES, NO); 127} 128 129- (void)sliderTouchStart:(RNCSlider *)sender 130{ 131 RNCSendSliderEvent(sender, NO, YES); 132 _isSliding = YES; 133 sender.isSliding = YES; 134} 135 136- (void)sliderTouchEnd:(RNCSlider *)sender 137{ 138 RNCSendSliderEvent(sender, NO, NO); 139 _isSliding = NO; 140 sender.isSliding = NO; 141} 142 143RCT_CUSTOM_VIEW_PROPERTY(value, float, RNCSlider) 144{ 145 if (!view.isSliding) { 146 view.value = [RCTConvert float:json]; 147 } 148} 149RCT_EXPORT_VIEW_PROPERTY(step, float); 150RCT_EXPORT_VIEW_PROPERTY(trackImage, UIImage); 151RCT_EXPORT_VIEW_PROPERTY(minimumTrackImage, UIImage); 152RCT_EXPORT_VIEW_PROPERTY(maximumTrackImage, UIImage); 153RCT_EXPORT_VIEW_PROPERTY(minimumValue, float); 154RCT_EXPORT_VIEW_PROPERTY(maximumValue, float); 155RCT_EXPORT_VIEW_PROPERTY(lowerLimit, float); 156RCT_EXPORT_VIEW_PROPERTY(upperLimit, float); 157RCT_EXPORT_VIEW_PROPERTY(minimumTrackTintColor, UIColor); 158RCT_EXPORT_VIEW_PROPERTY(maximumTrackTintColor, UIColor); 159RCT_EXPORT_VIEW_PROPERTY(onRNCSliderValueChange, RCTBubblingEventBlock); 160RCT_EXPORT_VIEW_PROPERTY(onRNCSliderSlidingStart, RCTBubblingEventBlock); 161RCT_EXPORT_VIEW_PROPERTY(onRNCSliderSlidingComplete, RCTBubblingEventBlock); 162RCT_EXPORT_VIEW_PROPERTY(thumbTintColor, UIColor); 163RCT_EXPORT_VIEW_PROPERTY(thumbImage, UIImage); 164RCT_EXPORT_VIEW_PROPERTY(inverted, BOOL); 165RCT_EXPORT_VIEW_PROPERTY(tapToSeek, BOOL); 166RCT_EXPORT_VIEW_PROPERTY(accessibilityUnits, NSString); 167RCT_EXPORT_VIEW_PROPERTY(accessibilityIncrements, NSArray); 168 169RCT_CUSTOM_VIEW_PROPERTY(disabled, BOOL, RNCSlider) 170{ 171 if (json) { 172 [view setDisabled: [RCTConvert BOOL:json]]; 173 } else { 174 [view setDisabled: defaultView.enabled]; 175 } 176} 177 178@end 179