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 */; };
A71946C81C7EFB69003B7C4A /* UIKit.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = E6F67FD018DE12B4001C954A /* UIKit.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 */
/* Begin PBXFileReference section */
@ -20,6 +28,14 @@
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>"; };
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; };
E6F67FD018DE12B4001C954A /* UIKit.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = UIKit.framework; path = System/Library/Frameworks/UIKit.framework; sourceTree = SDKROOT; };
/* End PBXFileReference section */
@ -44,6 +60,14 @@
A71946C11C7EFB1F003B7C4A /* COSTouchVisualizerWindow.h */,
A71946C21C7EFB1F003B7C4A /* COSTouchVisualizerWindow.m */,
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;
sourceTree = "<group>";
@ -81,7 +105,11 @@
isa = PBXHeadersBuildPhase;
buildActionMask = 2147483647;
files = (
E6C633A91FD0FB9D000DA40C /* COSOverlayVisualizerWindow.h in Headers */,
E6C633B51FD2D4FD000DA40C /* COSTouchImageFactory.h in Headers */,
E6C633AD1FD0FC8E000DA40C /* COSTouchImageView.h in Headers */,
A71946C51C7EFB1F003B7C4A /* COSTouchVisualizerWindow.h in Headers */,
E6C633B11FD2CCB2000DA40C /* COSTouchConfig.h in Headers */,
A71946C41C7EFB1F003B7C4A /* COSTouchVisualizer.h in Headers */,
);
runOnlyForDeploymentPostprocessing = 0;
@ -155,6 +183,10 @@
isa = PBXSourcesBuildPhase;
buildActionMask = 2147483647;
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 */,
);
runOnlyForDeploymentPostprocessing = 0;

View File

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

View File

@ -7,46 +7,12 @@
//
#import "COSTouchVisualizerWindow.h"
#import "COSOverlayVisualizerWindow.h"
#import "COSTouchImageView.h"
#import "COSTouchConfig.h"
#import "COSTouchImageFactory.h"
#pragma mark - Touch Visualizer Window
@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
static const NSTimeInterval COSTouchVisualizerWindowRemoveDelay = 0.2;
@interface COSTouchVisualizerWindow ()
@ -56,97 +22,43 @@
@property (nonatomic) NSTimer *timer;
@property (nonatomic) NSSet *allTouches;
- (void)COSTouchVisualizerWindow_commonInit;
- (void)scheduleFingerTipRemoval;
- (void)cancelScheduledFingerTipRemoval;
- (void)removeInactiveFingerTips;
- (void)removeFingerTipWithHash:(NSUInteger)hash animated:(BOOL)animated;
- (BOOL)shouldAutomaticallyRemoveFingerTipForTouch:(UITouch *)touch;
@property (nonatomic) UIImage *touchImage;
@property (nonatomic) UIImage *rippleImage;
@property (nonatomic) COSTouchConfig *touchContactConfig;
@property (nonatomic) COSTouchConfig *touchRippleConfig;
@end
@implementation COSTouchVisualizerWindow
- (id)initWithCoder:(NSCoder *)decoder {
// This covers NIB-loaded windows.
if (self = [super initWithCoder:decoder])
[self COSTouchVisualizerWindow_commonInit];
- (instancetype)initWithFrame:(CGRect)frame
morphEnabled:(BOOL)morphEnabled
touchVisibility:(COSTouchVisualizerWindowTouchVisibility)touchVisibility
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;
}
- (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
- (UIImage *)touchImage {
if (!_touchImage) {
UIBezierPath *clipPath = [UIBezierPath bezierPathWithRect:CGRectMake(0, 0, 50.0, 50.0)];
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();
_touchImage = [COSTouchImageFactory imageWithTouchConfig:self.touchContactConfig];
}
return _touchImage;
}
- (UIImage *)rippleImage {
if (!_rippleImage) {
UIBezierPath *clipPath = [UIBezierPath bezierPathWithRect:CGRectMake(0, 0, 50.0, 50.0)];
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();
_rippleImage = [COSTouchImageFactory imageWithTouchConfig:self.touchRippleConfig];
}
return _rippleImage;
}
@ -165,98 +77,94 @@
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
- (void)sendEvent:(UIEvent *)event {
if (self.active) {
self.allTouches = [event allTouches];
for (UITouch *touch in [self.allTouches allObjects]) {
switch (touch.phase) {
case UITouchPhaseBegan:
case UITouchPhaseMoved: {
// Generate ripples
COSTouchSpotView *rippleView = [[COSTouchSpotView alloc] initWithImage:self.rippleImage];
[self.overlayWindow addSubview:rippleView];
rippleView.alpha = self.rippleAlpha;
rippleView.center = [touch locationInView:self.overlayWindow];
[UIView animateWithDuration:self.rippleFadeDuration
delay:0.0
options:UIViewAnimationOptionCurveEaseIn // See other
// options
animations:^{
[rippleView setAlpha:0.0];
[rippleView setFrame:CGRectInset(rippleView.frame, 25, 25)];
} completion:^(BOOL finished) {
[rippleView removeFromSuperview];
}];
}
case UITouchPhaseStationary: {
COSTouchSpotView *touchView = (COSTouchSpotView *)[self.overlayWindow viewWithTag:touch.hash];
if (touch.phase != UITouchPhaseStationary && touchView != nil && [touchView isFadingOut]) {
[self.timer invalidate];
[touchView removeFromSuperview];
touchView = nil;
[super sendEvent:event];
switch (self.touchVisibility) {
case COSTouchVisualizerWindowTouchVisibilityNever:
return;
break;
case COSTouchVisualizerWindowTouchVisibilityRemoteOnly:
case COSTouchVisualizerWindowTouchVisibilityRemoteAndLocal: {
self.allTouches = [event allTouches];
for (UITouch *touch in [self.allTouches allObjects]) {
switch (touch.phase) {
case UITouchPhaseBegan:
case UITouchPhaseMoved: {
// Generate ripples
COSTouchImageView *rippleView = [[COSTouchImageView alloc] initWithImage:self.rippleImage];
[self.overlayWindow addSubview:rippleView];
rippleView.alpha = self.touchRippleConfig.alpha;
rippleView.center = [touch locationInView:self.overlayWindow];
[UIView animateWithDuration:self.touchRippleConfig.fadeDuration
delay:0.0
options:UIViewAnimationOptionCurveEaseIn
animations:^{
[rippleView setAlpha:0.0];
[rippleView setFrame:CGRectInset(rippleView.frame, 25, 25)];
} completion:^(BOOL finished) {
[rippleView removeFromSuperview];
}];
}
if (touchView == nil && touch.phase != UITouchPhaseStationary) {
touchView = [[COSTouchSpotView alloc] initWithImage:self.touchImage];
[self.overlayWindow addSubview:touchView];
if (self.stationaryMorphEnabled) {
if (self.timer) {
[self.timer invalidate];
}
self.timer = [NSTimer scheduledTimerWithTimeInterval:0.6
target:self
selector:@selector(performMorph:)
userInfo:touchView
repeats:YES];
case UITouchPhaseStationary: {
COSTouchImageView *touchView = (COSTouchImageView *)[self.overlayWindow viewWithTag:touch.hash];
if (touch.phase != UITouchPhaseStationary && touchView != nil && [touchView isFadingOut]) {
[self.timer invalidate];
[touchView removeFromSuperview];
touchView = nil;
}
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]) {
touchView.alpha = self.touchAlpha;
touchView.center = [touch locationInView:self.overlayWindow];
touchView.tag = touch.hash;
touchView.timestamp = touch.timestamp;
touchView.shouldAutomaticallyRemoveAfterTimeout = [self shouldAutomaticallyRemoveFingerTipForTouch:touch];
case UITouchPhaseEnded:
case UITouchPhaseCancelled: {
[self removeFingerTipWithHash:touch.hash animated:YES];
break;
}
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.
}
#pragma mark - Private
- (UIWindow *)overlayWindow {
if (!_overlayWindow) {
_overlayWindow = [[TouchVisualizerWindow alloc] initWithFrame:self.frame];
_overlayWindow = [[COSOverlayVisualizerWindow alloc] initWithFrame:self.frame];
_overlayWindow.userInteractionEnabled = NO;
_overlayWindow.windowLevel = UIWindowLevelStatusBar;
_overlayWindow.backgroundColor = [UIColor clearColor];
@ -264,11 +172,12 @@
}
return _overlayWindow;
}
#pragma mark - Private
- (void)scheduleFingerTipRemoval {
if (self.fingerTipRemovalScheduled)
if (self.fingerTipRemovalScheduled) {
return;
}
self.fingerTipRemovalScheduled = YES;
[self performSelector:@selector(removeInactiveFingerTips)
withObject:nil
@ -286,33 +195,34 @@
self.fingerTipRemovalScheduled = NO;
NSTimeInterval now = [[NSProcessInfo processInfo] systemUptime];
const CGFloat REMOVAL_DELAY = 0.2;
for (COSTouchSpotView *touchView in [self.overlayWindow subviews]) {
if (![touchView isKindOfClass:[COSTouchSpotView class]])
for (COSTouchImageView *touchView in [self.overlayWindow subviews]) {
if (![touchView isKindOfClass:[COSTouchImageView class]]) {
continue;
}
if (touchView.shouldAutomaticallyRemoveAfterTimeout && now > touchView.timestamp + REMOVAL_DELAY)
if (touchView.shouldAutomaticallyRemoveAfterTimeout && now > touchView.timestamp + COSTouchVisualizerWindowRemoveDelay) {
[self removeFingerTipWithHash:touchView.tag animated:YES];
}
}
if ([[self.overlayWindow subviews] count])
if ([[self.overlayWindow subviews] count]) {
[self scheduleFingerTipRemoval];
}
}
- (void)removeFingerTipWithHash:(NSUInteger)hash animated:(BOOL)animated {
COSTouchSpotView *touchView = (COSTouchSpotView *)[self.overlayWindow viewWithTag:hash];
if (touchView == nil)
return;
if ([touchView isFadingOut])
COSTouchImageView *touchView = (COSTouchImageView *)[self.overlayWindow viewWithTag:hash];
if (touchView == nil || [touchView isFadingOut]) {
return;
}
BOOL animationsWereEnabled = [UIView areAnimationsEnabled];
if (animated) {
[UIView setAnimationsEnabled:YES];
[UIView beginAnimations:nil context:nil];
[UIView setAnimationDuration:self.fadeDuration];
[UIView setAnimationDuration:self.touchContactConfig.fadeDuration];
}
touchView.frame = CGRectMake(touchView.center.x - touchView.frame.size.width,
@ -329,7 +239,7 @@
touchView.fadingOut = YES;
[touchView performSelector:@selector(removeFromSuperview)
withObject:nil
afterDelay:self.fadeDuration];
afterDelay:self.touchContactConfig.fadeDuration];
}
- (BOOL)shouldAutomaticallyRemoveFingerTipForTouch:(UITouch *)touch;
@ -346,40 +256,39 @@
// don't use UITouchPhaseStationary touches for those. *sigh*). So we
// end up with this more complicated setup.
UIView *view = [touch view];
view = [view hitTest:[touch locationInView:view] withEvent:nil];
UIView *touchView = [touch view];
touchView = [touchView hitTest:[touch locationInView:touchView] withEvent:nil];
while (view != nil) {
if ([view isKindOfClass:[UITableViewCell class]]) {
while (touchView != nil) {
if ([touchView isKindOfClass:[UITableViewCell class]]) {
for (UIGestureRecognizer *recognizer in [touch gestureRecognizers]) {
if ([recognizer isKindOfClass:[UISwipeGestureRecognizer class]])
return YES;
}
}
if ([view isKindOfClass:[UITableView class]]) {
if ([[touch gestureRecognizers] count] == 0)
return YES;
if ([touchView isKindOfClass:[UITableView class]] &&
[[touch gestureRecognizers] count] == 0) {
return YES;
}
view = view.superview;
touchView = touchView.superview;
}
return NO;
}
- (void)performMorph:(NSTimer *)theTimer {
UIView *view = (UIView *)[theTimer userInfo];
- (void)performMorphWithTouchView:(COSTouchImageView *)touchView {
NSTimeInterval duration = .4;
NSTimeInterval delay = 0;
// Start
view.alpha = self.touchAlpha;
view.transform = CGAffineTransformMakeScale(1, 1);
touchView.alpha = self.touchContactConfig.alpha;
touchView.transform = CGAffineTransformMakeScale(1, 1);
[UIView animateWithDuration:duration / 4
delay:delay
options:0
animations:^{
// End
view.transform = CGAffineTransformMakeScale(1, 1.2);
touchView.transform = CGAffineTransformMakeScale(1, 1.2);
}
completion:^(BOOL finished) {
[UIView animateWithDuration:duration / 4
@ -387,7 +296,7 @@
options:0
animations:^{
// End
view.transform = CGAffineTransformMakeScale(1.2, 0.9);
touchView.transform = CGAffineTransformMakeScale(1.2, 0.9);
}
completion:^(BOOL finished) {
[UIView animateWithDuration:duration / 4
@ -395,7 +304,7 @@
options:0
animations:^{
// End
view.transform = CGAffineTransformMakeScale(0.9, 0.9);
touchView.transform = CGAffineTransformMakeScale(0.9, 0.9);
}
completion:^(BOOL finished) {
[UIView animateWithDuration:duration / 4
@ -403,12 +312,13 @@
options:0
animations:^{
// End
view.transform = CGAffineTransformMakeScale(1, 1);
touchView.transform = CGAffineTransformMakeScale(1, 1);
}
completion:^(BOOL finished){
// If there are no touches, remove this morping touch
if (self.allTouches.count == 0)
[view removeFromSuperview];
if (self.allTouches.count == 0) {
[touchView removeFromSuperview];
}
}];
}];
}];

View File

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

View File

@ -314,13 +314,16 @@
files = (
);
inputPaths = (
"${PODS_PODFILE_DIR_PATH}/Podfile.lock",
"${PODS_ROOT}/Manifest.lock",
);
name = "[CP] Check Pods Manifest.lock";
outputPaths = (
"$(DERIVED_FILE_DIR)/Pods-TouchVisualizer-checkManifestLockResult.txt",
);
runOnlyForDeploymentPostprocessing = 0;
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;
};
/* End PBXShellScriptBuildPhase section */

View File

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