Refactor code to modern obj-c

This commit is contained in:
Joe Blau 2017-12-02 14:47:36 -05:00
parent ad729c993d
commit b00850925c
14 changed files with 424 additions and 263 deletions

View File

@ -0,0 +1,15 @@
//
// COSOverlayVisualizerWindow.h
// COSTouchVisualizer
//
// Created by Joseph Blau on 11/30/17.
// Copyright © 2017 conopsys. All rights reserved.
//
#import <UIKit/UIKit.h>
@interface COSOverlayVisualizerWindow : UIWindow
- (instancetype)initWithCoder:(NSCoder *)aDecoder NS_UNAVAILABLE;
@end

View File

@ -0,0 +1,31 @@
//
// COSOverlayVisualizerWindow.m
// COSTouchVisualizer
//
// Created by Joseph Blau on 11/30/17.
// Copyright © 2017 conopsys. All rights reserved.
//
#import "COSOverlayVisualizerWindow.h"
@implementation COSOverlayVisualizerWindow
// UIKit tries to get the rootViewController from the overlay window.
// Instead, try to find the rootViewController on some other
// application window.
// Fixes problems with status bar hiding, because it considers the
// overlay window a candidate for controlling the status bar.
- (UIViewController *)rootViewController {
for (UIWindow *window in [UIApplication sharedApplication].windows) {
if (self == window) {
continue;
}
if (window.rootViewController != nil) {
return window.rootViewController;
}
}
return [super rootViewController];
}
@end

26
Classes/COSTouchConfig.h Normal file
View File

@ -0,0 +1,26 @@
//
// COSTouchConfig.h
// COSTouchVisualizer
//
// Created by Joseph Blau on 12/2/17.
// Copyright © 2017 conopsys. All rights reserved.
//
#import <UIKit/UIKit.h>
typedef NS_ENUM(NSUInteger, COSTouchConfigTpye) {
COSTouchConfigTpyeContact,
COSTouchConfigTpyeRipple,
};
@interface COSTouchConfig : NSObject
@property (nonatomic) CGFloat alpha;
@property (nonatomic) NSTimeInterval fadeDuration;
@property (nonatomic, nullable) UIColor *strokeColor;
@property (nonatomic, nullable) UIColor *fillColor;
-(nonnull instancetype)initWithTouchConfigType:(COSTouchConfigTpye)configType NS_DESIGNATED_INITIALIZER;
-(nonnull instancetype)init NS_UNAVAILABLE;
@end

49
Classes/COSTouchConfig.m Normal file
View File

@ -0,0 +1,49 @@
//
// COSTouchConfig.m
// COSTouchVisualizer
//
// Created by Joseph Blau on 12/2/17.
// Copyright © 2017 conopsys. All rights reserved.
//
#import "COSTouchConfig.h"
static const CGFloat COSTouchConfigContactAlpha = 0.5f;
static const CGFloat COSTouchConfigRippleAlpha = 0.2f;
static const NSTimeInterval COSTouchConfigContactFadeDuration = 0.3;
static const NSTimeInterval COSTouchConfigRippleFadeDuration = 0.2;
@implementation COSTouchConfig
-(instancetype)initWithTouchConfigType:(COSTouchConfigTpye)configType {
self = [super init];
if (self) {
switch (configType) {
case COSTouchConfigTpyeContact:
[self _configureContact];
break;
case COSTouchConfigTpyeRipple:
[self _configureRipple];
break;
}
}
return self;
}
#pragma mark - Private
-(void)_configureContact {
self.strokeColor = [UIColor blackColor];
self.fillColor = [UIColor blackColor];
self.alpha = COSTouchConfigContactAlpha;
self.fadeDuration = COSTouchConfigContactFadeDuration;
}
- (void)_configureRipple {
self.strokeColor = [UIColor whiteColor];
self.fillColor = [UIColor blueColor];
self.alpha = COSTouchConfigRippleAlpha;
self.fadeDuration = COSTouchConfigRippleFadeDuration;
}
@end

View File

@ -0,0 +1,19 @@
//
// COSTouchImageFactory.h
// COSTouchVisualizer
//
// Created by Joseph Blau on 12/2/17.
// Copyright © 2017 conopsys. All rights reserved.
//
#import <UIKit/UIKit.h>
@class COSTouchConfig;
@interface COSTouchImageFactory : NSObject
+(nonnull UIImage *)imageWithTouchConfig:(nonnull COSTouchConfig*)touchConfig;
-(nonnull instancetype)init NS_UNAVAILABLE;
@end

View File

@ -0,0 +1,48 @@
//
// COSTouchImageFactory.m
// COSTouchVisualizer
//
// Created by Joseph Blau on 12/2/17.
// Copyright © 2017 conopsys. All rights reserved.
//
#import "COSTouchImageFactory.h"
#import "COSTouchConfig.h"
static const CGFloat COSTouchImageFactorySideSize = 50.0f;
@implementation COSTouchImageFactory
+(UIImage *)imageWithTouchConfig:(COSTouchConfig *)touchConfig {
UIImage *touchImage = ({
UIImage *image = [UIImage new];
UIBezierPath *clipPath = [UIBezierPath bezierPathWithRect:CGRectMake(0.0f,
0.0f,
COSTouchImageFactorySideSize,
COSTouchImageFactorySideSize)];
UIGraphicsBeginImageContextWithOptions(clipPath.bounds.size, NO, 0);
CGPoint center = CGPointMake(COSTouchImageFactorySideSize / 2.0f,
COSTouchImageFactorySideSize / 2.0f);
UIBezierPath *drawPath = [UIBezierPath bezierPathWithArcCenter:center
radius:22.0
startAngle:0
endAngle:2 * M_PI
clockwise:YES];
drawPath.lineWidth = 2.0;
[touchConfig.strokeColor setStroke];
[touchConfig.fillColor setFill];
[drawPath stroke];
[drawPath fill];
[clipPath addClip];
image = UIGraphicsGetImageFromCurrentImageContext();
UIGraphicsEndImageContext();
image;
});
return touchImage;
}
@end

View File

@ -0,0 +1,17 @@
//
// COSTouchSpotView.h
// COSTouchVisualizer
//
// Created by Joseph Blau on 11/30/17.
// Copyright © 2017 conopsys. All rights reserved.
//
#import <UIKit/UIKit.h>
@interface COSTouchImageView : UIImageView
@property (nonatomic) NSTimeInterval timestamp;
@property (nonatomic) BOOL shouldAutomaticallyRemoveAfterTimeout;
@property (nonatomic, getter=isFadingOut) BOOL fadingOut;
@end

View File

@ -0,0 +1,13 @@
//
// COSTouchSpotView.m
// COSTouchVisualizer
//
// Created by Joseph Blau on 11/30/17.
// Copyright © 2017 conopsys. All rights reserved.
//
#import "COSTouchImageView.h"
@implementation COSTouchImageView
@end

View File

@ -12,6 +12,14 @@
A71946C61C7EFB1F003B7C4A /* COSTouchVisualizerWindow.m in Sources */ = {isa = PBXBuildFile; fileRef = A71946C21C7EFB1F003B7C4A /* COSTouchVisualizerWindow.m */; }; A71946C61C7EFB1F003B7C4A /* COSTouchVisualizerWindow.m in Sources */ = {isa = PBXBuildFile; fileRef = A71946C21C7EFB1F003B7C4A /* COSTouchVisualizerWindow.m */; };
A71946C81C7EFB69003B7C4A /* UIKit.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = E6F67FD018DE12B4001C954A /* UIKit.framework */; }; A71946C81C7EFB69003B7C4A /* UIKit.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = E6F67FD018DE12B4001C954A /* UIKit.framework */; };
A71946C91C7EFB74003B7C4A /* Foundation.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = E6F67FCC18DE12B4001C954A /* Foundation.framework */; }; A71946C91C7EFB74003B7C4A /* Foundation.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = E6F67FCC18DE12B4001C954A /* Foundation.framework */; };
E6C633A91FD0FB9D000DA40C /* COSOverlayVisualizerWindow.h in Headers */ = {isa = PBXBuildFile; fileRef = E6C633A71FD0FB9D000DA40C /* COSOverlayVisualizerWindow.h */; };
E6C633AA1FD0FB9D000DA40C /* COSOverlayVisualizerWindow.m in Sources */ = {isa = PBXBuildFile; fileRef = E6C633A81FD0FB9D000DA40C /* COSOverlayVisualizerWindow.m */; };
E6C633AD1FD0FC8E000DA40C /* COSTouchImageView.h in Headers */ = {isa = PBXBuildFile; fileRef = E6C633AB1FD0FC8E000DA40C /* COSTouchImageView.h */; };
E6C633AE1FD0FC8E000DA40C /* COSTouchImageView.m in Sources */ = {isa = PBXBuildFile; fileRef = E6C633AC1FD0FC8E000DA40C /* COSTouchImageView.m */; };
E6C633B11FD2CCB2000DA40C /* COSTouchConfig.h in Headers */ = {isa = PBXBuildFile; fileRef = E6C633AF1FD2CCB2000DA40C /* COSTouchConfig.h */; };
E6C633B21FD2CCB2000DA40C /* COSTouchConfig.m in Sources */ = {isa = PBXBuildFile; fileRef = E6C633B01FD2CCB2000DA40C /* COSTouchConfig.m */; };
E6C633B51FD2D4FD000DA40C /* COSTouchImageFactory.h in Headers */ = {isa = PBXBuildFile; fileRef = E6C633B31FD2D4FD000DA40C /* COSTouchImageFactory.h */; };
E6C633B61FD2D4FD000DA40C /* COSTouchImageFactory.m in Sources */ = {isa = PBXBuildFile; fileRef = E6C633B41FD2D4FD000DA40C /* COSTouchImageFactory.m */; };
/* End PBXBuildFile section */ /* End PBXBuildFile section */
/* Begin PBXFileReference section */ /* Begin PBXFileReference section */
@ -20,6 +28,14 @@
A71946C11C7EFB1F003B7C4A /* COSTouchVisualizerWindow.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = COSTouchVisualizerWindow.h; sourceTree = "<group>"; }; A71946C11C7EFB1F003B7C4A /* COSTouchVisualizerWindow.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = COSTouchVisualizerWindow.h; sourceTree = "<group>"; };
A71946C21C7EFB1F003B7C4A /* COSTouchVisualizerWindow.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = COSTouchVisualizerWindow.m; sourceTree = "<group>"; }; A71946C21C7EFB1F003B7C4A /* COSTouchVisualizerWindow.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = COSTouchVisualizerWindow.m; sourceTree = "<group>"; };
A71946C31C7EFB1F003B7C4A /* Info.plist */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = "<group>"; }; A71946C31C7EFB1F003B7C4A /* Info.plist */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = "<group>"; };
E6C633A71FD0FB9D000DA40C /* COSOverlayVisualizerWindow.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = COSOverlayVisualizerWindow.h; sourceTree = "<group>"; };
E6C633A81FD0FB9D000DA40C /* COSOverlayVisualizerWindow.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = COSOverlayVisualizerWindow.m; sourceTree = "<group>"; };
E6C633AB1FD0FC8E000DA40C /* COSTouchImageView.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = COSTouchImageView.h; sourceTree = "<group>"; };
E6C633AC1FD0FC8E000DA40C /* COSTouchImageView.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = COSTouchImageView.m; sourceTree = "<group>"; };
E6C633AF1FD2CCB2000DA40C /* COSTouchConfig.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = COSTouchConfig.h; sourceTree = "<group>"; };
E6C633B01FD2CCB2000DA40C /* COSTouchConfig.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = COSTouchConfig.m; sourceTree = "<group>"; };
E6C633B31FD2D4FD000DA40C /* COSTouchImageFactory.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = COSTouchImageFactory.h; sourceTree = "<group>"; };
E6C633B41FD2D4FD000DA40C /* COSTouchImageFactory.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = COSTouchImageFactory.m; sourceTree = "<group>"; };
E6F67FCC18DE12B4001C954A /* Foundation.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = Foundation.framework; path = System/Library/Frameworks/Foundation.framework; sourceTree = SDKROOT; }; E6F67FCC18DE12B4001C954A /* Foundation.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = Foundation.framework; path = System/Library/Frameworks/Foundation.framework; sourceTree = SDKROOT; };
E6F67FD018DE12B4001C954A /* UIKit.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = UIKit.framework; path = System/Library/Frameworks/UIKit.framework; sourceTree = SDKROOT; }; E6F67FD018DE12B4001C954A /* UIKit.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = UIKit.framework; path = System/Library/Frameworks/UIKit.framework; sourceTree = SDKROOT; };
/* End PBXFileReference section */ /* End PBXFileReference section */
@ -44,6 +60,14 @@
A71946C11C7EFB1F003B7C4A /* COSTouchVisualizerWindow.h */, A71946C11C7EFB1F003B7C4A /* COSTouchVisualizerWindow.h */,
A71946C21C7EFB1F003B7C4A /* COSTouchVisualizerWindow.m */, A71946C21C7EFB1F003B7C4A /* COSTouchVisualizerWindow.m */,
A71946C31C7EFB1F003B7C4A /* Info.plist */, A71946C31C7EFB1F003B7C4A /* Info.plist */,
E6C633A71FD0FB9D000DA40C /* COSOverlayVisualizerWindow.h */,
E6C633A81FD0FB9D000DA40C /* COSOverlayVisualizerWindow.m */,
E6C633AB1FD0FC8E000DA40C /* COSTouchImageView.h */,
E6C633AC1FD0FC8E000DA40C /* COSTouchImageView.m */,
E6C633AF1FD2CCB2000DA40C /* COSTouchConfig.h */,
E6C633B01FD2CCB2000DA40C /* COSTouchConfig.m */,
E6C633B31FD2D4FD000DA40C /* COSTouchImageFactory.h */,
E6C633B41FD2D4FD000DA40C /* COSTouchImageFactory.m */,
); );
name = Source; name = Source;
sourceTree = "<group>"; sourceTree = "<group>";
@ -81,7 +105,11 @@
isa = PBXHeadersBuildPhase; isa = PBXHeadersBuildPhase;
buildActionMask = 2147483647; buildActionMask = 2147483647;
files = ( files = (
E6C633A91FD0FB9D000DA40C /* COSOverlayVisualizerWindow.h in Headers */,
E6C633B51FD2D4FD000DA40C /* COSTouchImageFactory.h in Headers */,
E6C633AD1FD0FC8E000DA40C /* COSTouchImageView.h in Headers */,
A71946C51C7EFB1F003B7C4A /* COSTouchVisualizerWindow.h in Headers */, A71946C51C7EFB1F003B7C4A /* COSTouchVisualizerWindow.h in Headers */,
E6C633B11FD2CCB2000DA40C /* COSTouchConfig.h in Headers */,
A71946C41C7EFB1F003B7C4A /* COSTouchVisualizer.h in Headers */, A71946C41C7EFB1F003B7C4A /* COSTouchVisualizer.h in Headers */,
); );
runOnlyForDeploymentPostprocessing = 0; runOnlyForDeploymentPostprocessing = 0;
@ -155,6 +183,10 @@
isa = PBXSourcesBuildPhase; isa = PBXSourcesBuildPhase;
buildActionMask = 2147483647; buildActionMask = 2147483647;
files = ( files = (
E6C633AA1FD0FB9D000DA40C /* COSOverlayVisualizerWindow.m in Sources */,
E6C633B61FD2D4FD000DA40C /* COSTouchImageFactory.m in Sources */,
E6C633B21FD2CCB2000DA40C /* COSTouchConfig.m in Sources */,
E6C633AE1FD0FC8E000DA40C /* COSTouchImageView.m in Sources */,
A71946C61C7EFB1F003B7C4A /* COSTouchVisualizerWindow.m in Sources */, A71946C61C7EFB1F003B7C4A /* COSTouchVisualizerWindow.m in Sources */,
); );
runOnlyForDeploymentPostprocessing = 0; runOnlyForDeploymentPostprocessing = 0;

View File

@ -7,36 +7,32 @@
// //
#include <UIKit/UIKit.h> #include <UIKit/UIKit.h>
@protocol COSTouchVisualizerWindowDelegate; typedef NS_ENUM(NSUInteger, COSTouchVisualizerWindowTouchVisibility) {
COSTouchVisualizerWindowTouchVisibilityNever,
COSTouchVisualizerWindowTouchVisibilityRemoteOnly,
COSTouchVisualizerWindowTouchVisibilityRemoteAndLocal,
};
@class COSTouchVisualizerWindow;
@class COSTouchConfig;
@interface COSTouchVisualizerWindow : UIWindow @interface COSTouchVisualizerWindow : UIWindow
@property (nonatomic, readonly, getter=isActive) BOOL active; @property (nonatomic, readonly, getter=isMorphEnabled) BOOL morphEnabled;
@property (nonatomic, weak) id<COSTouchVisualizerWindowDelegate> touchVisualizerWindowDelegate; @property (nonatomic, readonly, nonnull) COSTouchConfig *touchContactConfig;
@property (nonatomic, readonly, nonnull) COSTouchConfig *touchRippleConfig;
@property (nonatomic, readonly) COSTouchVisualizerWindowTouchVisibility touchVisibility;
// Touch effects -(nonnull instancetype)initWithFrame:(CGRect)frame
@property (nonatomic) UIImage *touchImage; morphEnabled:(BOOL)morphEnabled
@property (nonatomic) CGFloat touchAlpha; touchVisibility:(COSTouchVisualizerWindowTouchVisibility)touchVisibility
@property (nonatomic) NSTimeInterval fadeDuration; contactConfig:(nullable COSTouchConfig*)contactConfig
@property (nonatomic) UIColor *strokeColor; rippleConfig:(nullable COSTouchConfig*)rippleConfig NS_DESIGNATED_INITIALIZER;
@property (nonatomic) UIColor *fillColor; -(nonnull instancetype)initWithFrame:(CGRect)frame NS_UNAVAILABLE;
-(nonnull instancetype)init NS_UNAVAILABLE;
-(nonnull instancetype)initWithCoder:(nonnull NSCoder *)aDecoder NS_UNAVAILABLE;
// Ripple Effects
@property (nonatomic) UIImage *rippleImage;
@property (nonatomic) CGFloat rippleAlpha;
@property (nonatomic) NSTimeInterval rippleFadeDuration;
@property (nonatomic) UIColor *rippleStrokeColor;
@property (nonatomic) UIColor *rippleFillColor;
@property (nonatomic) BOOL stationaryMorphEnabled; // default: YES
@end @end
@protocol COSTouchVisualizerWindowDelegate <NSObject>
@optional
- (BOOL)touchVisualizerWindowShouldShowFingertip:(COSTouchVisualizerWindow *)window;
- (BOOL)touchVisualizerWindowShouldAlwaysShowFingertip:(COSTouchVisualizerWindow *)window;
@end

View File

@ -7,46 +7,12 @@
// //
#import "COSTouchVisualizerWindow.h" #import "COSTouchVisualizerWindow.h"
#import "COSOverlayVisualizerWindow.h"
#import "COSTouchImageView.h"
#import "COSTouchConfig.h"
#import "COSTouchImageFactory.h"
#pragma mark - Touch Visualizer Window static const NSTimeInterval COSTouchVisualizerWindowRemoveDelay = 0.2;
@interface TouchVisualizerWindow : UIWindow
@end
@implementation TouchVisualizerWindow
// UIKit tries to get the rootViewController from the overlay window.
// Instead, try to find the rootViewController on some other
// application window.
// Fixes problems with status bar hiding, because it considers the
// overlay window a candidate for controlling the status bar.
- (UIViewController *)rootViewController {
for (UIWindow *window in [[UIApplication sharedApplication] windows]) {
if (self == window)
continue;
UIViewController *realRootViewController = window.rootViewController;
if (realRootViewController != nil)
return realRootViewController;
}
return [super rootViewController];
}
@end
#pragma mark - Conopsys Touch Spot View
@interface COSTouchSpotView : UIImageView
@property (nonatomic) NSTimeInterval timestamp;
@property (nonatomic) BOOL shouldAutomaticallyRemoveAfterTimeout;
@property (nonatomic, getter=isFadingOut) BOOL fadingOut;
@end
@implementation COSTouchSpotView
@end
#pragma mark - Conopsys Touch Visualizer Window
@interface COSTouchVisualizerWindow () @interface COSTouchVisualizerWindow ()
@ -56,97 +22,43 @@
@property (nonatomic) NSTimer *timer; @property (nonatomic) NSTimer *timer;
@property (nonatomic) NSSet *allTouches; @property (nonatomic) NSSet *allTouches;
- (void)COSTouchVisualizerWindow_commonInit; @property (nonatomic) UIImage *touchImage;
- (void)scheduleFingerTipRemoval; @property (nonatomic) UIImage *rippleImage;
- (void)cancelScheduledFingerTipRemoval;
- (void)removeInactiveFingerTips; @property (nonatomic) COSTouchConfig *touchContactConfig;
- (void)removeFingerTipWithHash:(NSUInteger)hash animated:(BOOL)animated; @property (nonatomic) COSTouchConfig *touchRippleConfig;
- (BOOL)shouldAutomaticallyRemoveFingerTipForTouch:(UITouch *)touch;
@end @end
@implementation COSTouchVisualizerWindow @implementation COSTouchVisualizerWindow
- (id)initWithCoder:(NSCoder *)decoder { - (instancetype)initWithFrame:(CGRect)frame
// This covers NIB-loaded windows. morphEnabled:(BOOL)morphEnabled
if (self = [super initWithCoder:decoder]) touchVisibility:(COSTouchVisualizerWindowTouchVisibility)touchVisibility
[self COSTouchVisualizerWindow_commonInit]; contactConfig:(COSTouchConfig *)contactConfig
rippleConfig:(COSTouchConfig *)rippleConfig {
self = [super initWithFrame:frame];
if (self) {
_morphEnabled = morphEnabled;
_touchVisibility = touchVisibility;
_touchContactConfig = contactConfig ?: [[COSTouchConfig alloc] initWithTouchConfigType:COSTouchConfigTpyeContact];
_touchRippleConfig = rippleConfig ?: [[COSTouchConfig alloc] initWithTouchConfigType:COSTouchConfigTpyeRipple];
}
return self; return self;
} }
- (id)initWithFrame:(CGRect)rect {
// This covers programmatically-created windows.
if (self = [super initWithFrame:rect])
[self COSTouchVisualizerWindow_commonInit];
return self;
}
- (void)COSTouchVisualizerWindow_commonInit {
self.strokeColor = [UIColor blackColor];
self.fillColor = [UIColor whiteColor];
self.rippleStrokeColor = [UIColor whiteColor];
self.rippleFillColor = [UIColor blueColor];
self.touchAlpha = 0.5;
self.fadeDuration = 0.3;
self.rippleAlpha = 0.2;
self.rippleFadeDuration = 0.2;
self.stationaryMorphEnabled = YES;
}
#pragma mark - Touch / Ripple and Images #pragma mark - Touch / Ripple and Images
- (UIImage *)touchImage { - (UIImage *)touchImage {
if (!_touchImage) { if (!_touchImage) {
UIBezierPath *clipPath = [UIBezierPath bezierPathWithRect:CGRectMake(0, 0, 50.0, 50.0)]; _touchImage = [COSTouchImageFactory imageWithTouchConfig:self.touchContactConfig];
UIGraphicsBeginImageContextWithOptions(clipPath.bounds.size, NO, 0);
UIBezierPath *drawPath = [UIBezierPath bezierPathWithArcCenter:CGPointMake(25.0, 25.0)
radius:22.0
startAngle:0
endAngle:2 * M_PI
clockwise:YES];
drawPath.lineWidth = 2.0;
[self.strokeColor setStroke];
[self.fillColor setFill];
[drawPath stroke];
[drawPath fill];
[clipPath addClip];
_touchImage = UIGraphicsGetImageFromCurrentImageContext();
UIGraphicsEndImageContext();
} }
return _touchImage; return _touchImage;
} }
- (UIImage *)rippleImage { - (UIImage *)rippleImage {
if (!_rippleImage) { if (!_rippleImage) {
UIBezierPath *clipPath = [UIBezierPath bezierPathWithRect:CGRectMake(0, 0, 50.0, 50.0)]; _rippleImage = [COSTouchImageFactory imageWithTouchConfig:self.touchRippleConfig];
UIGraphicsBeginImageContextWithOptions(clipPath.bounds.size, NO, 0);
UIBezierPath *drawPath = [UIBezierPath bezierPathWithArcCenter:CGPointMake(25.0, 25.0)
radius:22.0
startAngle:0
endAngle:2 * M_PI
clockwise:YES];
drawPath.lineWidth = 2.0;
[self.rippleStrokeColor setStroke];
[self.rippleFillColor setFill];
[drawPath stroke];
[drawPath fill];
[clipPath addClip];
_rippleImage = UIGraphicsGetImageFromCurrentImageContext();
UIGraphicsEndImageContext();
} }
return _rippleImage; return _rippleImage;
} }
@ -165,98 +77,94 @@
return NO; return NO;
} }
- (BOOL)isActive {
// should show fingertip or not
if (![self.touchVisualizerWindowDelegate respondsToSelector:@selector(touchVisualizerWindowShouldShowFingertip:)] ||
[self.touchVisualizerWindowDelegate touchVisualizerWindowShouldShowFingertip:self]) {
// should always show or only when any screen is mirrored.
return (([self.touchVisualizerWindowDelegate respondsToSelector:@selector(touchVisualizerWindowShouldAlwaysShowFingertip:)] &&
[self.touchVisualizerWindowDelegate touchVisualizerWindowShouldAlwaysShowFingertip:self]) ||
[self anyScreenIsMirrored]);
} else {
return NO;
}
}
#pragma mark - UIWindow overrides #pragma mark - UIWindow overrides
- (void)sendEvent:(UIEvent *)event { - (void)sendEvent:(UIEvent *)event {
if (self.active) { [super sendEvent:event];
self.allTouches = [event allTouches];
for (UITouch *touch in [self.allTouches allObjects]) { switch (self.touchVisibility) {
switch (touch.phase) { case COSTouchVisualizerWindowTouchVisibilityNever:
case UITouchPhaseBegan: return;
case UITouchPhaseMoved: { break;
// Generate ripples
COSTouchSpotView *rippleView = [[COSTouchSpotView alloc] initWithImage:self.rippleImage]; case COSTouchVisualizerWindowTouchVisibilityRemoteOnly:
[self.overlayWindow addSubview:rippleView]; case COSTouchVisualizerWindowTouchVisibilityRemoteAndLocal: {
self.allTouches = [event allTouches];
rippleView.alpha = self.rippleAlpha; for (UITouch *touch in [self.allTouches allObjects]) {
rippleView.center = [touch locationInView:self.overlayWindow]; switch (touch.phase) {
case UITouchPhaseBegan:
[UIView animateWithDuration:self.rippleFadeDuration case UITouchPhaseMoved: {
delay:0.0 // Generate ripples
options:UIViewAnimationOptionCurveEaseIn // See other COSTouchImageView *rippleView = [[COSTouchImageView alloc] initWithImage:self.rippleImage];
// options [self.overlayWindow addSubview:rippleView];
animations:^{
[rippleView setAlpha:0.0]; rippleView.alpha = self.touchRippleConfig.alpha;
[rippleView setFrame:CGRectInset(rippleView.frame, 25, 25)]; rippleView.center = [touch locationInView:self.overlayWindow];
} completion:^(BOOL finished) {
[rippleView removeFromSuperview]; [UIView animateWithDuration:self.touchRippleConfig.fadeDuration
}]; delay:0.0
} options:UIViewAnimationOptionCurveEaseIn
case UITouchPhaseStationary: { animations:^{
COSTouchSpotView *touchView = (COSTouchSpotView *)[self.overlayWindow viewWithTag:touch.hash]; [rippleView setAlpha:0.0];
[rippleView setFrame:CGRectInset(rippleView.frame, 25, 25)];
if (touch.phase != UITouchPhaseStationary && touchView != nil && [touchView isFadingOut]) { } completion:^(BOOL finished) {
[self.timer invalidate]; [rippleView removeFromSuperview];
[touchView removeFromSuperview]; }];
touchView = nil;
} }
case UITouchPhaseStationary: {
if (touchView == nil && touch.phase != UITouchPhaseStationary) { COSTouchImageView *touchView = (COSTouchImageView *)[self.overlayWindow viewWithTag:touch.hash];
touchView = [[COSTouchSpotView alloc] initWithImage:self.touchImage];
[self.overlayWindow addSubview:touchView]; if (touch.phase != UITouchPhaseStationary && touchView != nil && [touchView isFadingOut]) {
[self.timer invalidate];
if (self.stationaryMorphEnabled) { [touchView removeFromSuperview];
if (self.timer) { touchView = nil;
[self.timer invalidate];
}
self.timer = [NSTimer scheduledTimerWithTimeInterval:0.6
target:self
selector:@selector(performMorph:)
userInfo:touchView
repeats:YES];
} }
if (touchView == nil && touch.phase != UITouchPhaseStationary) {
touchView = [[COSTouchImageView alloc] initWithImage:self.touchImage];
[self.overlayWindow addSubview:touchView];
if (self.morphEnabled) {
if (self.timer) {
[self.timer invalidate];
}
self.timer = [NSTimer scheduledTimerWithTimeInterval:0.6
target:self
selector:@selector(performMorphWithTouchView:)
userInfo:touchView
repeats:YES];
}
}
if (![touchView isFadingOut]) {
touchView.alpha = self.touchContactConfig.alpha;
touchView.center = [touch locationInView:self.overlayWindow];
touchView.tag = touch.hash;
touchView.timestamp = touch.timestamp;
touchView.shouldAutomaticallyRemoveAfterTimeout = [self shouldAutomaticallyRemoveFingerTipForTouch:touch];
}
break;
} }
if (![touchView isFadingOut]) { case UITouchPhaseEnded:
touchView.alpha = self.touchAlpha; case UITouchPhaseCancelled: {
touchView.center = [touch locationInView:self.overlayWindow]; [self removeFingerTipWithHash:touch.hash animated:YES];
touchView.tag = touch.hash; break;
touchView.timestamp = touch.timestamp;
touchView.shouldAutomaticallyRemoveAfterTimeout = [self shouldAutomaticallyRemoveFingerTipForTouch:touch];
} }
break;
}
case UITouchPhaseEnded:
case UITouchPhaseCancelled: {
[self removeFingerTipWithHash:touch.hash animated:YES];
break;
} }
} }
break;
} }
} }
[super sendEvent:event];
[self scheduleFingerTipRemoval]; // We may not see all UITouchPhaseEnded/UITouchPhaseCancelled events. [self scheduleFingerTipRemoval]; // We may not see all UITouchPhaseEnded/UITouchPhaseCancelled events.
} }
#pragma mark - Private
- (UIWindow *)overlayWindow { - (UIWindow *)overlayWindow {
if (!_overlayWindow) { if (!_overlayWindow) {
_overlayWindow = [[TouchVisualizerWindow alloc] initWithFrame:self.frame]; _overlayWindow = [[COSOverlayVisualizerWindow alloc] initWithFrame:self.frame];
_overlayWindow.userInteractionEnabled = NO; _overlayWindow.userInteractionEnabled = NO;
_overlayWindow.windowLevel = UIWindowLevelStatusBar; _overlayWindow.windowLevel = UIWindowLevelStatusBar;
_overlayWindow.backgroundColor = [UIColor clearColor]; _overlayWindow.backgroundColor = [UIColor clearColor];
@ -264,11 +172,12 @@
} }
return _overlayWindow; return _overlayWindow;
} }
#pragma mark - Private
- (void)scheduleFingerTipRemoval { - (void)scheduleFingerTipRemoval {
if (self.fingerTipRemovalScheduled) {
if (self.fingerTipRemovalScheduled)
return; return;
}
self.fingerTipRemovalScheduled = YES; self.fingerTipRemovalScheduled = YES;
[self performSelector:@selector(removeInactiveFingerTips) [self performSelector:@selector(removeInactiveFingerTips)
withObject:nil withObject:nil
@ -286,33 +195,34 @@
self.fingerTipRemovalScheduled = NO; self.fingerTipRemovalScheduled = NO;
NSTimeInterval now = [[NSProcessInfo processInfo] systemUptime]; NSTimeInterval now = [[NSProcessInfo processInfo] systemUptime];
const CGFloat REMOVAL_DELAY = 0.2;
for (COSTouchSpotView *touchView in [self.overlayWindow subviews]) { for (COSTouchImageView *touchView in [self.overlayWindow subviews]) {
if (![touchView isKindOfClass:[COSTouchSpotView class]]) if (![touchView isKindOfClass:[COSTouchImageView class]]) {
continue; continue;
}
if (touchView.shouldAutomaticallyRemoveAfterTimeout && now > touchView.timestamp + REMOVAL_DELAY) if (touchView.shouldAutomaticallyRemoveAfterTimeout && now > touchView.timestamp + COSTouchVisualizerWindowRemoveDelay) {
[self removeFingerTipWithHash:touchView.tag animated:YES]; [self removeFingerTipWithHash:touchView.tag animated:YES];
}
} }
if ([[self.overlayWindow subviews] count]) if ([[self.overlayWindow subviews] count]) {
[self scheduleFingerTipRemoval]; [self scheduleFingerTipRemoval];
}
} }
- (void)removeFingerTipWithHash:(NSUInteger)hash animated:(BOOL)animated { - (void)removeFingerTipWithHash:(NSUInteger)hash animated:(BOOL)animated {
COSTouchSpotView *touchView = (COSTouchSpotView *)[self.overlayWindow viewWithTag:hash]; COSTouchImageView *touchView = (COSTouchImageView *)[self.overlayWindow viewWithTag:hash];
if (touchView == nil) if (touchView == nil || [touchView isFadingOut]) {
return;
if ([touchView isFadingOut])
return; return;
}
BOOL animationsWereEnabled = [UIView areAnimationsEnabled]; BOOL animationsWereEnabled = [UIView areAnimationsEnabled];
if (animated) { if (animated) {
[UIView setAnimationsEnabled:YES]; [UIView setAnimationsEnabled:YES];
[UIView beginAnimations:nil context:nil]; [UIView beginAnimations:nil context:nil];
[UIView setAnimationDuration:self.fadeDuration]; [UIView setAnimationDuration:self.touchContactConfig.fadeDuration];
} }
touchView.frame = CGRectMake(touchView.center.x - touchView.frame.size.width, touchView.frame = CGRectMake(touchView.center.x - touchView.frame.size.width,
@ -329,7 +239,7 @@
touchView.fadingOut = YES; touchView.fadingOut = YES;
[touchView performSelector:@selector(removeFromSuperview) [touchView performSelector:@selector(removeFromSuperview)
withObject:nil withObject:nil
afterDelay:self.fadeDuration]; afterDelay:self.touchContactConfig.fadeDuration];
} }
- (BOOL)shouldAutomaticallyRemoveFingerTipForTouch:(UITouch *)touch; - (BOOL)shouldAutomaticallyRemoveFingerTipForTouch:(UITouch *)touch;
@ -346,40 +256,39 @@
// don't use UITouchPhaseStationary touches for those. *sigh*). So we // don't use UITouchPhaseStationary touches for those. *sigh*). So we
// end up with this more complicated setup. // end up with this more complicated setup.
UIView *view = [touch view]; UIView *touchView = [touch view];
view = [view hitTest:[touch locationInView:view] withEvent:nil]; touchView = [touchView hitTest:[touch locationInView:touchView] withEvent:nil];
while (view != nil) { while (touchView != nil) {
if ([view isKindOfClass:[UITableViewCell class]]) { if ([touchView isKindOfClass:[UITableViewCell class]]) {
for (UIGestureRecognizer *recognizer in [touch gestureRecognizers]) { for (UIGestureRecognizer *recognizer in [touch gestureRecognizers]) {
if ([recognizer isKindOfClass:[UISwipeGestureRecognizer class]]) if ([recognizer isKindOfClass:[UISwipeGestureRecognizer class]])
return YES; return YES;
} }
} }
if ([view isKindOfClass:[UITableView class]]) { if ([touchView isKindOfClass:[UITableView class]] &&
if ([[touch gestureRecognizers] count] == 0) [[touch gestureRecognizers] count] == 0) {
return YES; return YES;
} }
view = view.superview; touchView = touchView.superview;
} }
return NO; return NO;
} }
- (void)performMorph:(NSTimer *)theTimer { - (void)performMorphWithTouchView:(COSTouchImageView *)touchView {
UIView *view = (UIView *)[theTimer userInfo];
NSTimeInterval duration = .4; NSTimeInterval duration = .4;
NSTimeInterval delay = 0; NSTimeInterval delay = 0;
// Start // Start
view.alpha = self.touchAlpha; touchView.alpha = self.touchContactConfig.alpha;
view.transform = CGAffineTransformMakeScale(1, 1); touchView.transform = CGAffineTransformMakeScale(1, 1);
[UIView animateWithDuration:duration / 4 [UIView animateWithDuration:duration / 4
delay:delay delay:delay
options:0 options:0
animations:^{ animations:^{
// End // End
view.transform = CGAffineTransformMakeScale(1, 1.2); touchView.transform = CGAffineTransformMakeScale(1, 1.2);
} }
completion:^(BOOL finished) { completion:^(BOOL finished) {
[UIView animateWithDuration:duration / 4 [UIView animateWithDuration:duration / 4
@ -387,7 +296,7 @@
options:0 options:0
animations:^{ animations:^{
// End // End
view.transform = CGAffineTransformMakeScale(1.2, 0.9); touchView.transform = CGAffineTransformMakeScale(1.2, 0.9);
} }
completion:^(BOOL finished) { completion:^(BOOL finished) {
[UIView animateWithDuration:duration / 4 [UIView animateWithDuration:duration / 4
@ -395,7 +304,7 @@
options:0 options:0
animations:^{ animations:^{
// End // End
view.transform = CGAffineTransformMakeScale(0.9, 0.9); touchView.transform = CGAffineTransformMakeScale(0.9, 0.9);
} }
completion:^(BOOL finished) { completion:^(BOOL finished) {
[UIView animateWithDuration:duration / 4 [UIView animateWithDuration:duration / 4
@ -403,12 +312,13 @@
options:0 options:0
animations:^{ animations:^{
// End // End
view.transform = CGAffineTransformMakeScale(1, 1); touchView.transform = CGAffineTransformMakeScale(1, 1);
} }
completion:^(BOOL finished){ completion:^(BOOL finished){
// If there are no touches, remove this morping touch // If there are no touches, remove this morping touch
if (self.allTouches.count == 0) if (self.allTouches.count == 0) {
[view removeFromSuperview]; [touchView removeFromSuperview];
}
}]; }];
}]; }];
}]; }];

View File

@ -13,4 +13,4 @@ SPEC CHECKSUMS:
PODFILE CHECKSUM: 9c382a6853b1ccb79b91a8268f272eb3ffaff4be PODFILE CHECKSUM: 9c382a6853b1ccb79b91a8268f272eb3ffaff4be
COCOAPODS: 1.1.1 COCOAPODS: 1.3.1

View File

@ -314,13 +314,16 @@
files = ( files = (
); );
inputPaths = ( inputPaths = (
"${PODS_PODFILE_DIR_PATH}/Podfile.lock",
"${PODS_ROOT}/Manifest.lock",
); );
name = "[CP] Check Pods Manifest.lock"; name = "[CP] Check Pods Manifest.lock";
outputPaths = ( outputPaths = (
"$(DERIVED_FILE_DIR)/Pods-TouchVisualizer-checkManifestLockResult.txt",
); );
runOnlyForDeploymentPostprocessing = 0; runOnlyForDeploymentPostprocessing = 0;
shellPath = /bin/sh; shellPath = /bin/sh;
shellScript = "diff \"${PODS_ROOT}/../Podfile.lock\" \"${PODS_ROOT}/Manifest.lock\" > /dev/null\nif [ $? != 0 ] ; then\n # print error to STDERR\n echo \"error: The sandbox is not in sync with the Podfile.lock. Run 'pod install' or update your CocoaPods installation.\" >&2\n exit 1\nfi\n"; shellScript = "diff \"${PODS_PODFILE_DIR_PATH}/Podfile.lock\" \"${PODS_ROOT}/Manifest.lock\" > /dev/null\nif [ $? != 0 ] ; then\n # print error to STDERR\n echo \"error: The sandbox is not in sync with the Podfile.lock. Run 'pod install' or update your CocoaPods installation.\" >&2\n exit 1\nfi\n# This output is used by Xcode 'outputs' to avoid re-running this script phase.\necho \"SUCCESS\" > \"${SCRIPT_OUTPUT_FILE_0}\"\n";
showEnvVarsInLog = 0; showEnvVarsInLog = 0;
}; };
/* End PBXShellScriptBuildPhase section */ /* End PBXShellScriptBuildPhase section */

View File

@ -8,9 +8,7 @@
#import "COSAppDelegate.h" #import "COSAppDelegate.h"
#import <COSTouchVisualizerWindow.h> #import <COSTouchVisualizerWindow.h>
#import <COSTouchConfig.h>
@interface COSAppDelegate () <COSTouchVisualizerWindowDelegate>
@end
@implementation COSAppDelegate @implementation COSAppDelegate
@ -22,25 +20,29 @@
- (COSTouchVisualizerWindow *)window { - (COSTouchVisualizerWindow *)window {
static COSTouchVisualizerWindow *customWindow = nil; static COSTouchVisualizerWindow *customWindow = nil;
if (!customWindow) { if (!customWindow) {
customWindow = [[COSTouchVisualizerWindow alloc] initWithFrame:[[UIScreen mainScreen] bounds]]; COSTouchConfig *contactConfig = ({
COSTouchConfig *config = [[COSTouchConfig alloc] initWithTouchConfigType:COSTouchConfigTpyeContact];
config.fillColor = [UIColor purpleColor];
config.strokeColor = [UIColor blueColor];
config.alpha = 0.4;
config;
});
[customWindow setFillColor:[UIColor purpleColor]]; COSTouchConfig *riippleConfig = ({
[customWindow setStrokeColor:[UIColor blueColor]]; COSTouchConfig *config = [[COSTouchConfig alloc] initWithTouchConfigType:COSTouchConfigTpyeRipple];
[customWindow setTouchAlpha:0.4]; config.fillColor = [UIColor purpleColor];
config.strokeColor = [UIColor blueColor];
[customWindow setRippleFillColor:[UIColor purpleColor]]; config.alpha = 0.1;
[customWindow setRippleStrokeColor:[UIColor blueColor]]; config;
[customWindow setRippleAlpha:0.1]; });
[customWindow setTouchVisualizerWindowDelegate:self]; customWindow = [[COSTouchVisualizerWindow alloc] initWithFrame:[UIScreen mainScreen].bounds
morphEnabled:YES
touchVisibility:COSTouchVisualizerWindowTouchVisibilityRemoteAndLocal
contactConfig:contactConfig
rippleConfig:riippleConfig];
} }
return customWindow; return customWindow;
} }
#pragma mark - COSTouchVisualizerWindowDelegate
- (BOOL)touchVisualizerWindowShouldAlwaysShowFingertip:(COSTouchVisualizerWindow *)window {
return YES;
}
@end @end