├── README.md ├── TouchVisualizer.h ├── TouchVisualizer.m ├── TouchVisualizer.xcodeproj ├── project.pbxproj ├── project.xcworkspace │ ├── contents.xcworkspacedata │ └── xcuserdata │ │ └── zacharygibson.xcuserdatad │ │ └── UserInterfaceState.xcuserstate └── xcuserdata │ └── zacharygibson.xcuserdatad │ └── xcschemes │ ├── COSTouchVisualizerWindow.xcscheme │ └── xcschememanagement.plist └── package.json /README.md: -------------------------------------------------------------------------------- 1 | # React Native Touch Visualizer 2 | Customizable touch visualizer for app demos. 3 | Supports multiple presses and works on native iOS components; e.g. Action Sheets. 4 | 5 | ![React Native touch visualizer demo](https://cloud.githubusercontent.com/assets/10658888/21396686/a9f3ae52-c766-11e6-9423-3c6d97b4843f.gif) 6 | 7 | ## Install via npm 8 | ```bash 9 | $ npm install react-native-touch-visualizer --save 10 | ``` 11 | 12 | ## Link it to your project 13 | ```bash 14 | $ react-native link react-native-touch-visualizer 15 | ``` 16 | 17 | ## Edit AppDelegate.m 18 | ```objc 19 | #import "TouchVisualizer.h" 20 | ``` 21 | 22 | Change 23 | ```objc 24 | self.window = [[UIWindow alloc] initWithFrame:[UIScreen mainScreen].bounds]; 25 | ``` 26 | to 27 | ```objc 28 | self.window = [[TouchVisualizer alloc] initWithFrame:[UIScreen mainScreen].bounds]; 29 | ``` 30 | 31 | ## Copyright 32 | Copyright (c) 2017 Zachary Gibson Licensed under the MIT license. 33 | -------------------------------------------------------------------------------- /TouchVisualizer.h: -------------------------------------------------------------------------------- 1 | #import 2 | #include 3 | 4 | @protocol TouchVisualizerDelegate; 5 | 6 | @interface TouchVisualizer : UIWindow 7 | 8 | @property (nonatomic, weak) id touchVisualizerWindowDelegate; 9 | 10 | // Touch effects 11 | @property (nonatomic) UIImage *touchImage; 12 | @property (nonatomic) CGFloat touchAlpha; 13 | @property (nonatomic) NSTimeInterval fadeDuration; 14 | @property (nonatomic) UIColor *strokeColor; 15 | @property (nonatomic) UIColor *fillColor; 16 | 17 | // Ripple Effects 18 | @property (nonatomic) UIImage *rippleImage; 19 | @property (nonatomic) CGFloat rippleAlpha; 20 | @property (nonatomic) NSTimeInterval rippleFadeDuration; 21 | @property (nonatomic) UIColor *rippleStrokeColor; 22 | @property (nonatomic) UIColor *rippleFillColor; 23 | 24 | @end 25 | -------------------------------------------------------------------------------- /TouchVisualizer.m: -------------------------------------------------------------------------------- 1 | #import "TouchVisualizer.h" 2 | 3 | #pragma mark - Touch Visualizer Window 4 | 5 | @interface TouchVisualizerWindow : UIWindow 6 | @end 7 | 8 | @implementation TouchVisualizerWindow 9 | 10 | // UIKit tries to get the rootViewController from the overlay window. 11 | // Instead, try to find the rootViewController on some other 12 | // application window. 13 | // Fixes problems with status bar hiding, because it considers the 14 | // overlay window a candidate for controlling the status bar. 15 | - (UIViewController *)rootViewController { 16 | for (UIWindow *window in [[UIApplication sharedApplication] windows]) { 17 | if (self == window) 18 | continue; 19 | UIViewController *realRootViewController = window.rootViewController; 20 | if (realRootViewController != nil) 21 | return realRootViewController; 22 | } 23 | return [super rootViewController]; 24 | } 25 | 26 | @end 27 | 28 | #pragma mark - Conopsys Touch Spot View 29 | 30 | @interface COSTouchSpotView : UIImageView 31 | 32 | @property (nonatomic) NSTimeInterval timestamp; 33 | @property (nonatomic) BOOL shouldAutomaticallyRemoveAfterTimeout; 34 | @property (nonatomic, getter=isFadingOut) BOOL fadingOut; 35 | 36 | @end 37 | 38 | @implementation COSTouchSpotView 39 | @end 40 | 41 | #pragma mark - Conopsys Touch Visualizer Window 42 | 43 | @interface TouchVisualizer () 44 | 45 | @property (nonatomic) UIWindow *overlayWindow; 46 | @property (nonatomic) UIViewController *overlayWindowViewController; 47 | @property (nonatomic) BOOL fingerTipRemovalScheduled; 48 | @property (nonatomic) NSTimer *timer; 49 | @property (nonatomic) NSSet *allTouches; 50 | 51 | - (void)TouchVisualizer_commonInit; 52 | - (void)scheduleFingerTipRemoval; 53 | - (void)cancelScheduledFingerTipRemoval; 54 | - (void)removeInactiveFingerTips; 55 | - (void)removeFingerTipWithHash:(NSUInteger)hash animated:(BOOL)animated; 56 | - (BOOL)shouldAutomaticallyRemoveFingerTipForTouch:(UITouch *)touch; 57 | 58 | @end 59 | 60 | @implementation TouchVisualizer 61 | 62 | - (id)initWithCoder:(NSCoder *)decoder { 63 | // This covers NIB-loaded windows. 64 | if (self = [super initWithCoder:decoder]) 65 | [self TouchVisualizer_commonInit]; 66 | return self; 67 | } 68 | 69 | - (id)initWithFrame:(CGRect)rect { 70 | // This covers programmatically-created windows. 71 | if (self = [super initWithFrame:rect]) 72 | [self TouchVisualizer_commonInit]; 73 | return self; 74 | } 75 | 76 | - (void)TouchVisualizer_commonInit { 77 | self.strokeColor = [UIColor lightGrayColor]; 78 | self.fillColor = [UIColor whiteColor]; 79 | self.rippleStrokeColor = [UIColor whiteColor]; 80 | self.rippleFillColor = [UIColor blueColor]; 81 | self.touchAlpha = 0.5; 82 | self.fadeDuration = 0.3; 83 | self.rippleAlpha = 0.0; 84 | self.rippleFadeDuration = 0.0; 85 | } 86 | 87 | #pragma mark - Touch / Ripple and Images 88 | 89 | - (UIImage *)touchImage { 90 | if (!_touchImage) { 91 | UIBezierPath *clipPath = [UIBezierPath bezierPathWithRect:CGRectMake(0, 0, 50.0, 50.0)]; 92 | 93 | UIGraphicsBeginImageContextWithOptions(clipPath.bounds.size, NO, 0); 94 | 95 | UIBezierPath *drawPath = [UIBezierPath bezierPathWithArcCenter:CGPointMake(25.0, 25.0) 96 | radius:22.0 97 | startAngle:0 98 | endAngle:2 * M_PI 99 | clockwise:YES]; 100 | 101 | drawPath.lineWidth = 1.0; 102 | 103 | [self.strokeColor setStroke]; 104 | [self.fillColor setFill]; 105 | 106 | [drawPath stroke]; 107 | [drawPath fill]; 108 | 109 | [clipPath addClip]; 110 | 111 | _touchImage = UIGraphicsGetImageFromCurrentImageContext(); 112 | UIGraphicsEndImageContext(); 113 | } 114 | return _touchImage; 115 | } 116 | 117 | - (UIImage *)rippleImage { 118 | if (!_rippleImage) { 119 | UIBezierPath *clipPath = [UIBezierPath bezierPathWithRect:CGRectMake(0, 0, 50.0, 50.0)]; 120 | 121 | UIGraphicsBeginImageContextWithOptions(clipPath.bounds.size, NO, 0); 122 | 123 | UIBezierPath *drawPath = [UIBezierPath bezierPathWithArcCenter:CGPointMake(25.0, 25.0) 124 | radius:22.0 125 | startAngle:0 126 | endAngle:2 * M_PI 127 | clockwise:YES]; 128 | 129 | drawPath.lineWidth = 2.0; 130 | 131 | [self.rippleStrokeColor setStroke]; 132 | [self.rippleFillColor setFill]; 133 | 134 | [drawPath stroke]; 135 | [drawPath fill]; 136 | 137 | [clipPath addClip]; 138 | 139 | _rippleImage = UIGraphicsGetImageFromCurrentImageContext(); 140 | UIGraphicsEndImageContext(); 141 | } 142 | return _rippleImage; 143 | } 144 | 145 | #pragma mark - Active 146 | 147 | - (BOOL)anyScreenIsMirrored { 148 | if (![UIScreen instancesRespondToSelector:@selector(mirroredScreen)]) 149 | return NO; 150 | 151 | for (UIScreen *screen in [UIScreen screens]) { 152 | if ([screen mirroredScreen] != nil) { 153 | return YES; 154 | } 155 | } 156 | return NO; 157 | } 158 | 159 | #pragma mark - UIWindow overrides 160 | 161 | - (void)sendEvent:(UIEvent *)event { 162 | self.allTouches = [event allTouches]; 163 | for (UITouch *touch in [self.allTouches allObjects]) { 164 | switch (touch.phase) { 165 | case UITouchPhaseBegan: 166 | case UITouchPhaseMoved: { 167 | // Generate ripples 168 | COSTouchSpotView *rippleView = [[COSTouchSpotView alloc] initWithImage:self.rippleImage]; 169 | [self.overlayWindow addSubview:rippleView]; 170 | 171 | rippleView.alpha = self.rippleAlpha; 172 | rippleView.center = [touch locationInView:self.overlayWindow]; 173 | 174 | [UIView animateWithDuration:self.rippleFadeDuration 175 | delay:0.0 176 | options:UIViewAnimationOptionCurveEaseIn // See other 177 | // options 178 | animations:^{ 179 | [rippleView setAlpha:0.0]; 180 | [rippleView setFrame:CGRectInset(rippleView.frame, 25, 25)]; 181 | } completion:^(BOOL finished) { 182 | [rippleView removeFromSuperview]; 183 | }]; 184 | } 185 | case UITouchPhaseStationary: { 186 | COSTouchSpotView *touchView = (COSTouchSpotView *)[self.overlayWindow viewWithTag:touch.hash]; 187 | 188 | if (touch.phase != UITouchPhaseStationary && touchView != nil && [touchView isFadingOut]) { 189 | [self.timer invalidate]; 190 | [touchView removeFromSuperview]; 191 | touchView = nil; 192 | } 193 | 194 | if (touchView == nil && touch.phase != UITouchPhaseStationary) { 195 | touchView = [[COSTouchSpotView alloc] initWithImage:self.touchImage]; 196 | [self.overlayWindow addSubview:touchView]; 197 | } 198 | if (![touchView isFadingOut]) { 199 | touchView.alpha = self.touchAlpha; 200 | touchView.center = [touch locationInView:self.overlayWindow]; 201 | touchView.tag = touch.hash; 202 | touchView.timestamp = touch.timestamp; 203 | touchView.shouldAutomaticallyRemoveAfterTimeout = [self shouldAutomaticallyRemoveFingerTipForTouch:touch]; 204 | } 205 | break; 206 | } 207 | case UITouchPhaseEnded: 208 | case UITouchPhaseCancelled: { 209 | [self removeFingerTipWithHash:touch.hash animated:YES]; 210 | break; 211 | } 212 | } 213 | } 214 | 215 | [super sendEvent:event]; 216 | [self scheduleFingerTipRemoval]; // We may not see all UITouchPhaseEnded/UITouchPhaseCancelled events. 217 | } 218 | 219 | #pragma mark - Private 220 | 221 | - (UIWindow *)overlayWindow { 222 | if (!_overlayWindow) { 223 | _overlayWindow = [[TouchVisualizerWindow alloc] initWithFrame:self.frame]; 224 | _overlayWindow.userInteractionEnabled = NO; 225 | _overlayWindow.windowLevel = UIWindowLevelStatusBar; 226 | _overlayWindow.backgroundColor = [UIColor clearColor]; 227 | _overlayWindow.hidden = NO; 228 | } 229 | return _overlayWindow; 230 | } 231 | 232 | - (void)scheduleFingerTipRemoval { 233 | 234 | if (self.fingerTipRemovalScheduled) 235 | return; 236 | self.fingerTipRemovalScheduled = YES; 237 | [self performSelector:@selector(removeInactiveFingerTips) 238 | withObject:nil 239 | afterDelay:0.1]; 240 | } 241 | 242 | - (void)cancelScheduledFingerTipRemoval { 243 | self.fingerTipRemovalScheduled = YES; 244 | [NSObject cancelPreviousPerformRequestsWithTarget:self 245 | selector:@selector(removeInactiveFingerTips) 246 | object:nil]; 247 | } 248 | 249 | - (void)removeInactiveFingerTips { 250 | self.fingerTipRemovalScheduled = NO; 251 | 252 | NSTimeInterval now = [[NSProcessInfo processInfo] systemUptime]; 253 | const CGFloat REMOVAL_DELAY = 0.2; 254 | for (COSTouchSpotView *touchView in [self.overlayWindow subviews]) { 255 | if (![touchView isKindOfClass:[COSTouchSpotView class]]) 256 | continue; 257 | 258 | if (touchView.shouldAutomaticallyRemoveAfterTimeout && now > touchView.timestamp + REMOVAL_DELAY) 259 | [self removeFingerTipWithHash:touchView.tag animated:YES]; 260 | } 261 | 262 | if ([[self.overlayWindow subviews] count]) 263 | [self scheduleFingerTipRemoval]; 264 | } 265 | 266 | - (void)removeFingerTipWithHash:(NSUInteger)hash animated:(BOOL)animated { 267 | COSTouchSpotView *touchView = (COSTouchSpotView *)[self.overlayWindow viewWithTag:hash]; 268 | if (touchView == nil) 269 | return; 270 | 271 | if ([touchView isFadingOut]) 272 | return; 273 | 274 | BOOL animationsWereEnabled = [UIView areAnimationsEnabled]; 275 | 276 | if (animated) { 277 | [UIView setAnimationsEnabled:YES]; 278 | [UIView beginAnimations:nil context:nil]; 279 | [UIView setAnimationDuration:self.fadeDuration]; 280 | } 281 | 282 | touchView.frame = CGRectMake(touchView.center.x - touchView.frame.size.width, 283 | touchView.center.y - touchView.frame.size.height, 284 | touchView.frame.size.width * 2, touchView.frame.size.height * 2); 285 | 286 | touchView.alpha = 0.0; 287 | 288 | if (animated) { 289 | [UIView commitAnimations]; 290 | [UIView setAnimationsEnabled:animationsWereEnabled]; 291 | } 292 | 293 | touchView.fadingOut = YES; 294 | [touchView performSelector:@selector(removeFromSuperview) 295 | withObject:nil 296 | afterDelay:self.fadeDuration]; 297 | } 298 | 299 | - (BOOL)shouldAutomaticallyRemoveFingerTipForTouch:(UITouch *)touch; 300 | { 301 | // We don't reliably get UITouchPhaseEnded or UITouchPhaseCancelled 302 | // events via -sendEvent: for certain touch events. Known cases 303 | // include swipe-to-delete on a table view row, and tap-to-cancel 304 | // swipe to delete. We automatically remove their associated 305 | // fingertips after a suitable timeout. 306 | // 307 | // It would be much nicer if we could remove all touch events after 308 | // a suitable time out, but then we'll prematurely remove touch and 309 | // hold events that are picked up by gesture recognizers (since we 310 | // don't use UITouchPhaseStationary touches for those. *sigh*). So we 311 | // end up with this more complicated setup. 312 | 313 | UIView *view = [touch view]; 314 | view = [view hitTest:[touch locationInView:view] withEvent:nil]; 315 | 316 | while (view != nil) { 317 | if ([view isKindOfClass:[UITableViewCell class]]) { 318 | for (UIGestureRecognizer *recognizer in [touch gestureRecognizers]) { 319 | if ([recognizer isKindOfClass:[UISwipeGestureRecognizer class]]) 320 | return YES; 321 | } 322 | } 323 | 324 | if ([view isKindOfClass:[UITableView class]]) { 325 | if ([[touch gestureRecognizers] count] == 0) 326 | return YES; 327 | } 328 | view = view.superview; 329 | } 330 | 331 | return NO; 332 | } 333 | 334 | RCT_EXPORT_MODULE() 335 | 336 | @end 337 | -------------------------------------------------------------------------------- /TouchVisualizer.xcodeproj/project.pbxproj: -------------------------------------------------------------------------------- 1 | // !$*UTF8*$! 2 | { 3 | archiveVersion = 1; 4 | classes = { 5 | }; 6 | objectVersion = 46; 7 | objects = { 8 | 9 | /* Begin PBXBuildFile section */ 10 | 13BE3DEE1AC21097009241FE /* TouchVisualizer.m in Sources */ = {isa = PBXBuildFile; fileRef = 13BE3DED1AC21097009241FE /* TouchVisualizer.m */; }; 11 | /* End PBXBuildFile section */ 12 | 13 | /* Begin PBXCopyFilesBuildPhase section */ 14 | 58B511D91A9E6C8500147676 /* CopyFiles */ = { 15 | isa = PBXCopyFilesBuildPhase; 16 | buildActionMask = 2147483647; 17 | dstPath = "include/$(PRODUCT_NAME)"; 18 | dstSubfolderSpec = 16; 19 | files = ( 20 | ); 21 | runOnlyForDeploymentPostprocessing = 0; 22 | }; 23 | /* End PBXCopyFilesBuildPhase section */ 24 | 25 | /* Begin PBXFileReference section */ 26 | 134814201AA4EA6300B7C361 /* libTouchVisualizer.a */ = {isa = PBXFileReference; explicitFileType = archive.ar; includeInIndex = 0; path = libTouchVisualizer.a; sourceTree = BUILT_PRODUCTS_DIR; }; 27 | 13BE3DEC1AC21097009241FE /* TouchVisualizer.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = TouchVisualizer.h; sourceTree = ""; }; 28 | 13BE3DED1AC21097009241FE /* TouchVisualizer.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = TouchVisualizer.m; sourceTree = ""; }; 29 | /* End PBXFileReference section */ 30 | 31 | /* Begin PBXFrameworksBuildPhase section */ 32 | 58B511D81A9E6C8500147676 /* Frameworks */ = { 33 | isa = PBXFrameworksBuildPhase; 34 | buildActionMask = 2147483647; 35 | files = ( 36 | ); 37 | runOnlyForDeploymentPostprocessing = 0; 38 | }; 39 | /* End PBXFrameworksBuildPhase section */ 40 | 41 | /* Begin PBXGroup section */ 42 | 134814211AA4EA7D00B7C361 /* Products */ = { 43 | isa = PBXGroup; 44 | children = ( 45 | 134814201AA4EA6300B7C361 /* libTouchVisualizer.a */, 46 | ); 47 | name = Products; 48 | sourceTree = ""; 49 | }; 50 | 58B511D21A9E6C8500147676 = { 51 | isa = PBXGroup; 52 | children = ( 53 | 13BE3DEC1AC21097009241FE /* TouchVisualizer.h */, 54 | 13BE3DED1AC21097009241FE /* TouchVisualizer.m */, 55 | 134814211AA4EA7D00B7C361 /* Products */, 56 | ); 57 | sourceTree = ""; 58 | }; 59 | /* End PBXGroup section */ 60 | 61 | /* Begin PBXNativeTarget section */ 62 | 58B511DA1A9E6C8500147676 /* TouchVisualizer */ = { 63 | isa = PBXNativeTarget; 64 | buildConfigurationList = 58B511EF1A9E6C8500147676 /* Build configuration list for PBXNativeTarget "TouchVisualizer" */; 65 | buildPhases = ( 66 | 58B511D71A9E6C8500147676 /* Sources */, 67 | 58B511D81A9E6C8500147676 /* Frameworks */, 68 | 58B511D91A9E6C8500147676 /* CopyFiles */, 69 | ); 70 | buildRules = ( 71 | ); 72 | dependencies = ( 73 | ); 74 | name = TouchVisualizer; 75 | productName = RCTDataManager; 76 | productReference = 134814201AA4EA6300B7C361 /* libTouchVisualizer.a */; 77 | productType = "com.apple.product-type.library.static"; 78 | }; 79 | /* End PBXNativeTarget section */ 80 | 81 | /* Begin PBXProject section */ 82 | 58B511D31A9E6C8500147676 /* Project object */ = { 83 | isa = PBXProject; 84 | attributes = { 85 | LastUpgradeCheck = 0610; 86 | ORGANIZATIONNAME = Facebook; 87 | TargetAttributes = { 88 | 58B511DA1A9E6C8500147676 = { 89 | CreatedOnToolsVersion = 6.1.1; 90 | }; 91 | }; 92 | }; 93 | buildConfigurationList = 58B511D61A9E6C8500147676 /* Build configuration list for PBXProject "TouchVisualizer" */; 94 | compatibilityVersion = "Xcode 3.2"; 95 | developmentRegion = English; 96 | hasScannedForEncodings = 0; 97 | knownRegions = ( 98 | en, 99 | ); 100 | mainGroup = 58B511D21A9E6C8500147676; 101 | productRefGroup = 58B511D21A9E6C8500147676; 102 | projectDirPath = ""; 103 | projectRoot = ""; 104 | targets = ( 105 | 58B511DA1A9E6C8500147676 /* TouchVisualizer */, 106 | ); 107 | }; 108 | /* End PBXProject section */ 109 | 110 | /* Begin PBXSourcesBuildPhase section */ 111 | 58B511D71A9E6C8500147676 /* Sources */ = { 112 | isa = PBXSourcesBuildPhase; 113 | buildActionMask = 2147483647; 114 | files = ( 115 | 13BE3DEE1AC21097009241FE /* TouchVisualizer.m in Sources */, 116 | ); 117 | runOnlyForDeploymentPostprocessing = 0; 118 | }; 119 | /* End PBXSourcesBuildPhase section */ 120 | 121 | /* Begin XCBuildConfiguration section */ 122 | 58B511ED1A9E6C8500147676 /* Debug */ = { 123 | isa = XCBuildConfiguration; 124 | buildSettings = { 125 | ALWAYS_SEARCH_USER_PATHS = NO; 126 | CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x"; 127 | CLANG_CXX_LIBRARY = "libc++"; 128 | CLANG_ENABLE_MODULES = YES; 129 | CLANG_ENABLE_OBJC_ARC = YES; 130 | CLANG_WARN_BOOL_CONVERSION = YES; 131 | CLANG_WARN_CONSTANT_CONVERSION = YES; 132 | CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; 133 | CLANG_WARN_EMPTY_BODY = YES; 134 | CLANG_WARN_ENUM_CONVERSION = YES; 135 | CLANG_WARN_INT_CONVERSION = YES; 136 | CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; 137 | CLANG_WARN_UNREACHABLE_CODE = YES; 138 | CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; 139 | COPY_PHASE_STRIP = NO; 140 | ENABLE_STRICT_OBJC_MSGSEND = YES; 141 | GCC_C_LANGUAGE_STANDARD = gnu99; 142 | GCC_DYNAMIC_NO_PIC = NO; 143 | GCC_OPTIMIZATION_LEVEL = 0; 144 | GCC_PREPROCESSOR_DEFINITIONS = ( 145 | "DEBUG=1", 146 | "$(inherited)", 147 | ); 148 | GCC_SYMBOLS_PRIVATE_EXTERN = NO; 149 | GCC_WARN_64_TO_32_BIT_CONVERSION = YES; 150 | GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; 151 | GCC_WARN_UNDECLARED_SELECTOR = YES; 152 | GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; 153 | GCC_WARN_UNUSED_FUNCTION = YES; 154 | GCC_WARN_UNUSED_VARIABLE = YES; 155 | IPHONEOS_DEPLOYMENT_TARGET = 8.0; 156 | MTL_ENABLE_DEBUG_INFO = YES; 157 | ONLY_ACTIVE_ARCH = YES; 158 | SDKROOT = iphoneos; 159 | }; 160 | name = Debug; 161 | }; 162 | 58B511EE1A9E6C8500147676 /* Release */ = { 163 | isa = XCBuildConfiguration; 164 | buildSettings = { 165 | ALWAYS_SEARCH_USER_PATHS = NO; 166 | CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x"; 167 | CLANG_CXX_LIBRARY = "libc++"; 168 | CLANG_ENABLE_MODULES = YES; 169 | CLANG_ENABLE_OBJC_ARC = YES; 170 | CLANG_WARN_BOOL_CONVERSION = YES; 171 | CLANG_WARN_CONSTANT_CONVERSION = YES; 172 | CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; 173 | CLANG_WARN_EMPTY_BODY = YES; 174 | CLANG_WARN_ENUM_CONVERSION = YES; 175 | CLANG_WARN_INT_CONVERSION = YES; 176 | CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; 177 | CLANG_WARN_UNREACHABLE_CODE = YES; 178 | CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; 179 | COPY_PHASE_STRIP = YES; 180 | ENABLE_NS_ASSERTIONS = NO; 181 | ENABLE_STRICT_OBJC_MSGSEND = YES; 182 | GCC_C_LANGUAGE_STANDARD = gnu99; 183 | GCC_WARN_64_TO_32_BIT_CONVERSION = YES; 184 | GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; 185 | GCC_WARN_UNDECLARED_SELECTOR = YES; 186 | GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; 187 | GCC_WARN_UNUSED_FUNCTION = YES; 188 | GCC_WARN_UNUSED_VARIABLE = YES; 189 | IPHONEOS_DEPLOYMENT_TARGET = 8.0; 190 | MTL_ENABLE_DEBUG_INFO = NO; 191 | SDKROOT = iphoneos; 192 | VALIDATE_PRODUCT = YES; 193 | }; 194 | name = Release; 195 | }; 196 | 58B511F01A9E6C8500147676 /* Debug */ = { 197 | isa = XCBuildConfiguration; 198 | buildSettings = { 199 | HEADER_SEARCH_PATHS = ( 200 | "$(inherited)", 201 | /Applications/Xcode.app/Contents/Developer/Toolchains/XcodeDefault.xctoolchain/usr/include, 202 | "$(SRCROOT)/../../React/**", 203 | "$(SRCROOT)/../../node_modules/react-native/React/**", 204 | ); 205 | LIBRARY_SEARCH_PATHS = "$(inherited)"; 206 | OTHER_LDFLAGS = "-ObjC"; 207 | PRODUCT_NAME = TouchVisualizer; 208 | SKIP_INSTALL = YES; 209 | }; 210 | name = Debug; 211 | }; 212 | 58B511F11A9E6C8500147676 /* Release */ = { 213 | isa = XCBuildConfiguration; 214 | buildSettings = { 215 | HEADER_SEARCH_PATHS = ( 216 | "$(inherited)", 217 | /Applications/Xcode.app/Contents/Developer/Toolchains/XcodeDefault.xctoolchain/usr/include, 218 | "$(SRCROOT)/../../React/**", 219 | ); 220 | LIBRARY_SEARCH_PATHS = "$(inherited)"; 221 | OTHER_LDFLAGS = "-ObjC"; 222 | PRODUCT_NAME = TouchVisualizer; 223 | SKIP_INSTALL = YES; 224 | }; 225 | name = Release; 226 | }; 227 | /* End XCBuildConfiguration section */ 228 | 229 | /* Begin XCConfigurationList section */ 230 | 58B511D61A9E6C8500147676 /* Build configuration list for PBXProject "TouchVisualizer" */ = { 231 | isa = XCConfigurationList; 232 | buildConfigurations = ( 233 | 58B511ED1A9E6C8500147676 /* Debug */, 234 | 58B511EE1A9E6C8500147676 /* Release */, 235 | ); 236 | defaultConfigurationIsVisible = 0; 237 | defaultConfigurationName = Release; 238 | }; 239 | 58B511EF1A9E6C8500147676 /* Build configuration list for PBXNativeTarget "TouchVisualizer" */ = { 240 | isa = XCConfigurationList; 241 | buildConfigurations = ( 242 | 58B511F01A9E6C8500147676 /* Debug */, 243 | 58B511F11A9E6C8500147676 /* Release */, 244 | ); 245 | defaultConfigurationIsVisible = 0; 246 | defaultConfigurationName = Release; 247 | }; 248 | /* End XCConfigurationList section */ 249 | }; 250 | rootObject = 58B511D31A9E6C8500147676 /* Project object */; 251 | } 252 | -------------------------------------------------------------------------------- /TouchVisualizer.xcodeproj/project.xcworkspace/contents.xcworkspacedata: -------------------------------------------------------------------------------- 1 | 2 | 4 | 6 | 7 | 8 | -------------------------------------------------------------------------------- /TouchVisualizer.xcodeproj/project.xcworkspace/xcuserdata/zacharygibson.xcuserdatad/UserInterfaceState.xcuserstate: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/zachgibson/react-native-touch-visualizer/448668895214bc4ca5ff0ac8e5be63effffa3a6e/TouchVisualizer.xcodeproj/project.xcworkspace/xcuserdata/zacharygibson.xcuserdatad/UserInterfaceState.xcuserstate -------------------------------------------------------------------------------- /TouchVisualizer.xcodeproj/xcuserdata/zacharygibson.xcuserdatad/xcschemes/COSTouchVisualizerWindow.xcscheme: -------------------------------------------------------------------------------- 1 | 2 | 5 | 8 | 9 | 15 | 21 | 22 | 23 | 24 | 25 | 30 | 31 | 32 | 33 | 34 | 35 | 45 | 46 | 52 | 53 | 54 | 55 | 56 | 57 | 63 | 64 | 70 | 71 | 72 | 73 | 75 | 76 | 79 | 80 | 81 | -------------------------------------------------------------------------------- /TouchVisualizer.xcodeproj/xcuserdata/zacharygibson.xcuserdatad/xcschemes/xcschememanagement.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | SchemeUserState 6 | 7 | TouchVisualizer.xcscheme 8 | 9 | orderHint 10 | 20 11 | 12 | 13 | SuppressBuildableAutocreation 14 | 15 | 58B511DA1A9E6C8500147676 16 | 17 | primary 18 | 19 | 20 | 21 | 22 | 23 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "react-native-touch-visualizer", 3 | "version": "0.0.1", 4 | "keywords": [ 5 | "react-native", 6 | "react", 7 | "native", 8 | "touch", 9 | "react-component", 10 | "react-native-component", 11 | "touch-visualizer", 12 | "mobile" 13 | ], 14 | "description": "Visualize touches and drags in React Native apps.", 15 | "main": "TouchVisualizer.xcodeproj", 16 | "scripts": { 17 | "test": "echo \"Error: no test specified\" && exit 1" 18 | }, 19 | "author": "Zach Gibson (http://zachgibsondesign.com/)", 20 | "license": "MIT" 21 | } 22 | --------------------------------------------------------------------------------