Last active
December 22, 2015 12:59
-
-
Save Shilo/6476241 to your computer and use it in GitHub Desktop.
A UIScrollView subclass that will allow a specified GLKViewController to continue rendering, while scrolling. It will also allow touches to be passed to a Sparrow view if property 'passTouchEventsToSparrow' is YES.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
// | |
// XDSPScrollView.h | |
// A scroll view that will allow a specified GLKViewController to continue rendering, while scrolling. | |
// It will also allow touches to be passed to a Sparrow view if property 'passTouchEventsToSparrow' is YES. | |
// | |
// Created by Shilo White on 9/7/13. | |
// | |
#import <UIKit/UIKit.h> | |
@class GLKViewController; | |
@interface XDSPScrollView : UIScrollView | |
@property (nonatomic, weak) GLKViewController *glkViewController; | |
@property (nonatomic, assign) BOOL passTouchEventsToSparrow; | |
@property (nonatomic, readonly) BOOL rendering; | |
@property (nonatomic, readonly) BOOL active; | |
@end |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
// | |
// XDSPScrollView.m | |
// A scroll view that will allow a specified GLKViewController to continue rendering, while scrolling. | |
// It will also allow touches to be passed to a Sparrow view if property 'passTouchEventsToSparrow' is YES. | |
// | |
// Created by Shilo White on 9/7/13. | |
// | |
#import "XDSPScrollView.h" | |
#import <GLKit/GLKit.h> | |
#import <QuartzCore/QuartzCore.h> | |
#import <objc/runtime.h> | |
#import "SPViewController.h" | |
#import "SPTouch_Internal.h" | |
#import "SPTouchProcessor.h" | |
static NSMutableArray *sActiveScrollViews; | |
@interface GLKViewController (XDSPScrollView) | |
@property (nonatomic, weak) XDSPScrollView *_activeScrollView; | |
@end | |
@implementation GLKViewController (XDSPScrollView) | |
- (void)set_activeScrollView:(XDSPScrollView *)_activeScrollView | |
{ | |
objc_setAssociatedObject(self, @selector(_activeScrollView), _activeScrollView, OBJC_ASSOCIATION_ASSIGN); | |
} | |
- (XDSPScrollView *)_activeScrollView | |
{ | |
return objc_getAssociatedObject(self, @selector(_activeScrollView)); | |
} | |
@end | |
@interface XDSPScrollView () <UIScrollViewDelegate> | |
@property (nonatomic, assign) BOOL rendering; | |
@property (nonatomic, assign) BOOL active; | |
+ (NSMutableArray *)activeScrollViews; | |
+ (void)addActiveScrollView:(XDSPScrollView *)scrollView; | |
+ (void)removeActiveScrollView:(XDSPScrollView *)scrollView; | |
+ (XDSPScrollView *)activeScrollViewWithGLKViewController:(GLKViewController *)glkViewController; | |
@end | |
@implementation XDSPScrollView { | |
id<UIScrollViewDelegate> __unsafe_unretained _delegate; | |
GLKViewController __weak *_glkViewController; | |
CADisplayLink *_displayLink; | |
BOOL _passTouchEventsToSparrow; | |
BOOL _active; | |
} | |
@synthesize delegate = _delegate; | |
@synthesize glkViewController = _glkViewController; | |
@synthesize passTouchEventsToSparrow = _passTouchEventsToSparrow; | |
@synthesize active = _active; | |
- (id)initWithFrame:(CGRect)frame | |
{ | |
if ((self = [super initWithFrame:frame])) | |
{ | |
[self setup]; | |
} | |
return self; | |
} | |
- (id)initWithCoder:(NSCoder *)aDecoder | |
{ | |
if ((self = [super initWithCoder:aDecoder])) | |
{ | |
[self setup]; | |
} | |
return self; | |
} | |
- (id)init | |
{ | |
if ((self = [super init])) | |
{ | |
[self setup]; | |
} | |
return self; | |
} | |
- (void)setup | |
{ | |
super.delegate = self; | |
_glkViewController = nil; | |
_displayLink = nil; | |
_active = NO; | |
} | |
- (void)setRendering:(BOOL)rendering | |
{ | |
if (rendering != self.rendering) | |
{ | |
[_displayLink invalidate]; | |
if (rendering) | |
{ | |
_displayLink = [CADisplayLink displayLinkWithTarget:self selector:@selector(render)]; | |
_displayLink.frameInterval = (60/_glkViewController.preferredFramesPerSecond); | |
[_displayLink addToRunLoop:[NSRunLoop currentRunLoop] forMode:NSRunLoopCommonModes]; | |
} | |
else | |
_displayLink = nil; | |
} | |
} | |
- (BOOL)rendering | |
{ | |
return (_displayLink != nil); | |
} | |
- (void)setActive:(BOOL)active | |
{ | |
if (active != _active) | |
{ | |
_active = active; | |
if (_active) | |
[XDSPScrollView addActiveScrollView:self]; | |
else | |
[XDSPScrollView removeActiveScrollView:self]; | |
} | |
} | |
- (void)setPassTouchEventsToSparrow:(BOOL)passTouchEventsToSparrow | |
{ | |
if (passTouchEventsToSparrow != _passTouchEventsToSparrow) | |
{ | |
_passTouchEventsToSparrow = passTouchEventsToSparrow; | |
self.delaysContentTouches = !passTouchEventsToSparrow; | |
} | |
} | |
- (void)render | |
{ | |
[_glkViewController performSelector:@selector(_updateAndDraw)]; | |
} | |
+ (NSMutableArray *)activeScrollViews | |
{ | |
if (!sActiveScrollViews) | |
sActiveScrollViews = [[NSMutableArray alloc] init]; | |
return sActiveScrollViews; | |
} | |
+ (void)addActiveScrollView:(XDSPScrollView *)scrollView | |
{ | |
GLKViewController *glkViewController = scrollView.glkViewController; | |
if (glkViewController && glkViewController._activeScrollView == nil) | |
{ | |
glkViewController._activeScrollView = scrollView; | |
scrollView.rendering = YES; | |
} | |
[[self activeScrollViews] addObject:scrollView]; | |
} | |
+ (void)removeActiveScrollView:(XDSPScrollView *)scrollView | |
{ | |
[[self activeScrollViews] removeObject:scrollView]; | |
GLKViewController *glkViewController = scrollView.glkViewController; | |
if (glkViewController._activeScrollView == scrollView) | |
{ | |
scrollView.rendering = NO; | |
XDSPScrollView *activeScrollView = [self activeScrollViewWithGLKViewController:glkViewController]; | |
glkViewController._activeScrollView = activeScrollView; | |
activeScrollView.rendering = YES; | |
} | |
} | |
+ (XDSPScrollView *)activeScrollViewWithGLKViewController:(GLKViewController *)glkViewController | |
{ | |
for (XDSPScrollView *activeScrollView in sActiveScrollViews) | |
if (activeScrollView.glkViewController == glkViewController) | |
return activeScrollView; | |
return nil; | |
} | |
- (void)scrollViewDidScroll:(UIScrollView *)scrollView | |
{ | |
if ([_delegate respondsToSelector:@selector(scrollViewDidScroll:)]) | |
[_delegate scrollViewDidScroll:scrollView]; | |
} | |
- (void)scrollViewWillBeginDragging:(UIScrollView *)scrollView | |
{ | |
self.active = YES; | |
if ([_delegate respondsToSelector:@selector(scrollViewWillBeginDragging:)]) | |
[_delegate scrollViewWillBeginDragging:scrollView]; | |
} | |
- (void)scrollViewWillEndDragging:(UIScrollView *)scrollView withVelocity:(CGPoint)velocity targetContentOffset:(inout CGPoint *)targetContentOffset | |
{ | |
if ([_delegate respondsToSelector:@selector(scrollViewWillEndDragging:withVelocity:targetContentOffset:)]) | |
[_delegate scrollViewWillEndDragging:scrollView withVelocity:velocity targetContentOffset:targetContentOffset]; | |
} | |
- (void)scrollViewDidEndDragging:(UIScrollView *)scrollView willDecelerate:(BOOL)decelerate | |
{ | |
if (!decelerate) self.active = NO; | |
if ([_delegate respondsToSelector:@selector(scrollViewDidEndDragging:willDecelerate:)]) | |
[_delegate scrollViewDidEndDragging:scrollView willDecelerate:decelerate]; | |
} | |
- (BOOL)scrollViewShouldScrollToTop:(UIScrollView *)scrollView | |
{ | |
if ([_delegate respondsToSelector:@selector(scrollViewShouldScrollToTop:)]) | |
return [_delegate scrollViewShouldScrollToTop:scrollView]; | |
else | |
return YES; | |
} | |
- (void)scrollViewDidScrollToTop:(UIScrollView *)scrollView | |
{ | |
if ([_delegate respondsToSelector:@selector(scrollViewDidScrollToTop:)]) | |
[_delegate scrollViewDidScrollToTop:scrollView]; | |
} | |
- (void)scrollViewWillBeginDecelerating:(UIScrollView *)scrollView | |
{ | |
if ([_delegate respondsToSelector:@selector(scrollViewWillBeginDecelerating:)]) | |
[_delegate scrollViewWillBeginDecelerating:scrollView]; | |
} | |
- (void)scrollViewDidEndDecelerating:(UIScrollView *)scrollView | |
{ | |
self.active = NO; | |
if ([_delegate respondsToSelector:@selector(scrollViewDidEndDecelerating:)]) | |
[_delegate scrollViewDidEndDecelerating:scrollView]; | |
} | |
- (UIView *)viewForZoomingInScrollView:(UIScrollView *)scrollView | |
{ | |
if ([_delegate respondsToSelector:@selector(viewForZoomingInScrollView:)]) | |
return [_delegate viewForZoomingInScrollView:scrollView]; | |
else | |
return nil; | |
} | |
- (void)scrollViewWillBeginZooming:(UIScrollView *)scrollView withView:(UIView *)view | |
{ | |
if ([_delegate respondsToSelector:@selector(scrollViewWillBeginZooming:withView:)]) | |
[_delegate scrollViewWillBeginZooming:scrollView withView:view]; | |
} | |
- (void)scrollViewDidEndZooming:(UIScrollView *)scrollView withView:(UIView *)view atScale:(float)scale | |
{ | |
if ([_delegate respondsToSelector:@selector(scrollViewDidEndZooming:withView:atScale:)]) | |
[_delegate scrollViewDidEndZooming:scrollView withView:view atScale:scale]; | |
} | |
- (void)scrollViewDidZoom:(UIScrollView *)scrollView | |
{ | |
if ([_delegate respondsToSelector:@selector(scrollViewDidZoom:)]) | |
[_delegate scrollViewDidZoom:scrollView]; | |
} | |
- (void)scrollViewDidEndScrollingAnimation:(UIScrollView *)scrollView | |
{ | |
if ([_delegate respondsToSelector:@selector(scrollViewDidEndScrollingAnimation:)]) | |
[_delegate scrollViewDidEndScrollingAnimation:scrollView]; | |
} | |
@end | |
@interface SPViewController () { | |
@public | |
SPTouchProcessor *_touchProcessor; | |
double _lastTouchTimestamp; | |
} | |
@end | |
@implementation UIScrollView (PassTouchEventsToSparrow) | |
- (void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event | |
{ | |
[self processTouchEvent:event]; | |
} | |
- (void)touchesMoved:(NSSet *)touches withEvent:(UIEvent *)event | |
{ | |
[self processTouchEvent:event]; | |
} | |
- (void)touchesEnded:(NSSet *)touches withEvent:(UIEvent *)event | |
{ | |
[self processTouchEvent:event]; | |
} | |
- (void)touchesCancelled:(NSSet *)touches withEvent:(UIEvent *)event | |
{ | |
[self processTouchEvent:event cancelled:YES]; | |
} | |
- (void)processTouchEvent:(UIEvent*)event | |
{ | |
[self processTouchEvent:event cancelled:NO]; | |
} | |
- (void)processTouchEvent:(UIEvent*)event cancelled:(BOOL)cancelled | |
{ | |
XDSPScrollView *Self = nil; | |
if ([self isKindOfClass:[XDSPScrollView class]]) | |
{ | |
Self = (XDSPScrollView *)self; | |
if (Self.passTouchEventsToSparrow && [Self.glkViewController isKindOfClass:[SPViewController class]]) | |
{ | |
SPViewController *sparrowController = (SPViewController *)Self.glkViewController; | |
if (cancelled) | |
{ | |
Sparrow.currentController->_lastTouchTimestamp -= 0.0001f; // cancelled touch events have an old timestamp -> workaround | |
} | |
if (!sparrowController.paused && sparrowController->_lastTouchTimestamp != event.timestamp) | |
{ | |
@autoreleasepool | |
{ | |
UIView *sparrowView = sparrowController.view; | |
CGSize viewSize = sparrowView.bounds.size; | |
float xConversion = Sparrow.stage.width / viewSize.width; | |
float yConversion = Sparrow.stage.height / viewSize.height; | |
// convert to SPTouches and forward to stage | |
NSMutableSet *touches = [NSMutableSet set]; | |
double now = CACurrentMediaTime(); | |
for (UITouch *uiTouch in [event allTouches]) | |
{ | |
UIView *touchedView = uiTouch.view; | |
CGPoint location = [sparrowView convertPoint:[uiTouch locationInView:touchedView] fromView:touchedView]; | |
CGPoint previousLocation = [sparrowView convertPoint:[uiTouch previousLocationInView:touchedView] fromView:touchedView]; | |
SPTouch *touch = [SPTouch touch]; | |
touch.timestamp = now; // timestamp of uiTouch not compatible to Sparrow timestamp | |
touch.globalX = location.x * xConversion; | |
touch.globalY = location.y * yConversion; | |
touch.previousGlobalX = previousLocation.x * xConversion; | |
touch.previousGlobalY = previousLocation.y * yConversion; | |
touch.tapCount = uiTouch.tapCount; | |
touch.phase = (SPTouchPhase)uiTouch.phase; | |
[touches addObject:touch]; | |
} | |
[sparrowController->_touchProcessor processTouches:touches]; | |
sparrowController->_lastTouchTimestamp = event.timestamp; | |
} | |
} | |
} | |
} | |
} | |
@end |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment