blob: 34f0c6963e63edb3fdc700bc17cff5db83520774 [file] [log] [blame]
/*
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. The ASF licenses this file
* to you under the Apache License, Version 2.0 (the
* "License"); you may not use this file except in compliance
* with the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
* KIND, either express or implied. See the License for the
* specific language governing permissions and limitations
* under the License.
*/
#import "WXComponent+Events.h"
#import "WXComponent.h"
#import "WXComponent_internal.h"
#import "WXSDKInstance.h"
#import "WXComponentManager.h"
#import "WXAssert.h"
#import "WXUtility.h"
#import "WXSDKManager.h"
#import "WXSDKInstance_private.h"
#import "WXDefine.h"
#import "WXRecycleListComponent.h"
#import "WXRecycleListDataManager.h"
#import <objc/runtime.h>
#import <UIKit/UIGestureRecognizerSubclass.h>
#import "WXComponent+PseudoClassManagement.h"
#import "WXCoreBridge.h"
#pragma clang diagnostic ignored "-Wobjc-protocol-method-implementation"
@interface UITouch (WXTouchGestureRecognizer)
@property (nonatomic, strong) NSNumber *wx_identifier;
@property (nonatomic, strong) NSNumber *wx_stopPropagation;
@end
@implementation UITouch (WXTouchGestureRecognizer)
- (NSNumber *)wx_identifier
{
return objc_getAssociatedObject(self, _cmd);
}
- (void)setWx_identifier:(NSNumber *)wx_identifier
{
objc_setAssociatedObject(self, @selector(wx_identifier), wx_identifier, OBJC_ASSOCIATION_RETAIN_NONATOMIC);
}
- (NSNumber *)wx_stopPropagation
{
return objc_getAssociatedObject(self, _cmd);
}
- (void)setWx_stopPropagation:(NSNumber *)wx_stopPropagation
{
objc_setAssociatedObject(self, @selector(wx_stopPropagation), wx_stopPropagation, OBJC_ASSOCIATION_RETAIN_NONATOMIC);
}
@end
@interface UIGestureRecognizer (WXGesture)
@property (nonatomic, strong) NSNumber *wx_identifier;
@end
@implementation UIGestureRecognizer (WXGesture)
- (NSNumber *)wx_identifier
{
NSNumber *identifier = objc_getAssociatedObject(self, _cmd);
if (!identifier) {
static NSUInteger _gestureIdentifier;
identifier = @(_gestureIdentifier++);
self.wx_identifier = identifier;
}
return identifier;
}
- (void)setWx_identifier:(NSNumber *)wx_identifier
{
objc_setAssociatedObject(self, @selector(wx_identifier), wx_identifier, OBJC_ASSOCIATION_RETAIN_NONATOMIC);
}
@end
@interface WXTouchGestureRecognizer : UIGestureRecognizer
@property (nonatomic, assign) BOOL listenTouchStart;
@property (nonatomic, assign) BOOL listenTouchMove;
@property (nonatomic, assign) BOOL listenTouchEnd;
@property (nonatomic, assign) BOOL listenTouchCancel;
@property (nonatomic, assign) BOOL listenPseudoTouch;
- (instancetype)initWithComponent:(WXComponent *)component NS_DESIGNATED_INITIALIZER;
@end
@interface WXEventManager :NSObject
+ (instancetype) sharedManager;
- (BOOL)stopPropagation:(NSString *)instanceId ref:(NSString *)ref type:(NSString *)type params:(NSDictionary *)params;
@end
@implementation WXEventManager
- (instancetype) init
{
self = [super init];
return self;
}
+ (instancetype)sharedManager
{
static id _sharedInstance = nil;
static dispatch_once_t oncePredicate;
dispatch_once(&oncePredicate, ^{
_sharedInstance = [[self alloc] init];
});
return _sharedInstance;
}
- (BOOL)stopPropagation:(NSString *)instanceId ref:(NSString *)ref type:(NSString *)type params:(NSDictionary *)params
{
JSValue *value = [[WXSDKManager bridgeMgr] fireEventWithResult:instanceId ref:ref type:type params:params domChanges:nil];
if ([value.toString isEqualToString:@"true"]) {
return YES;
}
return NO;
}
@end
@implementation WXComponent (Events)
#pragma mark Public
- (void)fireEvent:(NSString *)eventName params:(NSDictionary *)params
{
[self fireEvent:eventName params:params domChanges:nil];
}
- (void)fireEvent:(NSString *)eventName params:(NSDictionary *)params domChanges:(NSDictionary *)domChanges
{
NSMutableDictionary *dict = [NSMutableDictionary dictionary];
NSTimeInterval timeSp = [[NSDate date] timeIntervalSince1970] * 1000;
[dict setObject:@(timeSp) forKey:@"timestamp"];
if (params) {
[dict addEntriesFromDictionary:params];
}
WXRecycleListComponent *recyleListComponent = (WXRecycleListComponent*)[self getRecycleListComponent];
if (recyleListComponent) {
NSIndexPath *indexPath = [((UICollectionView*)recyleListComponent.view) indexPathForItemAtPoint:[self.view.superview
convertPoint:self.view.center toView:recyleListComponent.view]];
NSString *templateId = [self recursiveFindTemplateIdWithComponent:self];
NSString *virtualComponentId = [recyleListComponent.dataManager virtualComponentIdWithIndexPath:indexPath templateId:templateId];
if (virtualComponentId) {
dict[@"componentId"] = virtualComponentId;
}
}
NSArray *handlerArguments = [self _paramsForEvent:eventName];
NSString *ref = _templateComponent ? _templateComponent.ref : self.ref;
[[WXSDKManager bridgeMgr] fireEvent:self.weexInstance.instanceId ref:ref type:eventName params:dict domChanges:domChanges handlerArguments:handlerArguments];
}
- (NSString *)recursiveFindTemplateIdWithComponent:(WXComponent *)component
{
if (!component) {
return nil;
}
if ([component isKindOfClass:NSClassFromString(@"WXCellSlotComponent")]) {
return nil;
}
if (component.attributes[@"@templateId"]) {
return component.attributes[@"@templateId"];
}
return [self recursiveFindTemplateIdWithComponent:component.supercomponent];
}
- (void)addEvent:(NSString *)addEventName
{
WXAssertMainThread();
}
- (void)removeEvent:(NSString *)removeEventName
{
WXAssertMainThread();
}
#pragma mark Add & Remove Event
#define WX_ADD_EVENT(eventName, addSelector) \
if ([addEventName isEqualToString:@#eventName]) {\
[self addSelector];\
}
#define WX_ADD_EVENTS(eventName1,eventName2, addSelector) \
if ([addEventName isEqualToString:@#eventName1]||[addEventName isEqualToString:@#eventName2]) {\
[self addSelector:addEventName];\
}
#define WX_REMOVE_EVENT(eventName, removeSelector) \
if ([removeEventName isEqualToString:@#eventName]) {\
[self removeSelector];\
}
#define WX_REMOVE_EVENTS(eventName1,eventName2, removeSelector) \
if ([removeEventName isEqualToString:@#eventName1]||[removeEventName isEqualToString:@#eventName2]) {\
[self removeSelector];\
}
- (void)_initEvents:(NSArray *)events
{
for (NSString *addEventName in events) {
[self _addEventOnMainThread:addEventName];
}
}
- (void)_initPseudoEvents:(BOOL)isListenPseudoTouch
{
if(isListenPseudoTouch) {
self.touchGesture.listenPseudoTouch = YES;
}
}
- (void)_addEventOnMainThread:(NSString *)addEventName
{
if (![self isViewLoaded]) {
//This action will be ignored While the view is loaded,
//then it will initEvent according to the records in _events
return;
}
WX_ADD_EVENT(appear, addAppearEvent)
WX_ADD_EVENT(disappear, addDisappearEvent)
WX_ADD_EVENT(click, addClickEvent)
WX_ADD_EVENT(swipe, addSwipeEvent)
WX_ADD_EVENT(longpress, addLongPressEvent)
WX_ADD_EVENT(panstart, addPanStartEvent)
WX_ADD_EVENT(panmove, addPanMoveEvent)
WX_ADD_EVENT(panend, addPanEndEvent)
WX_ADD_EVENT(horizontalpan, addHorizontalPanEvent)
WX_ADD_EVENT(verticalpan, addVerticalPanEvent)
WX_ADD_EVENT(touchstart, addTouchStartEvent)
WX_ADD_EVENT(touchmove, addTouchMoveEvent)
WX_ADD_EVENT(touchend, addTouchEndEvent)
WX_ADD_EVENT(touchcancel, addTouchCancelEvent)
WX_ADD_EVENT(accessibilityMagicTap, addAccessibilityMagicTapEvent)
WX_ADD_EVENTS(stopPropagation, stoppropagation, addStopPropagationEvent)
if(_isListenPseudoTouch) {
self.touchGesture.listenPseudoTouch = YES;
}
[self addEvent:addEventName];
}
- (void)_removeEventOnMainThread:(NSString *)removeEventName
{
WX_REMOVE_EVENT(appear, removeAppearEvent)
WX_REMOVE_EVENT(disappear, removeDisappearEvent)
WX_REMOVE_EVENT(click, removeClickEvent)
WX_REMOVE_EVENT(swipe, removeSwipeEvent)
WX_REMOVE_EVENT(longpress, removeLongPressEvent)
WX_REMOVE_EVENT(panstart, removePanStartEvent)
WX_REMOVE_EVENT(panmove, removePanMoveEvent)
WX_REMOVE_EVENT(panend, removePanEndEvent)
WX_REMOVE_EVENT(horizontalpan, removeHorizontalPanEvent)
WX_REMOVE_EVENT(verticalpan, removeVerticalPanEvent)
WX_REMOVE_EVENT(touchstart, removeTouchStartEvent)
WX_REMOVE_EVENT(touchmove, removeTouchMoveEvent)
WX_REMOVE_EVENT(touchend, removeTouchEndEvent)
WX_REMOVE_EVENT(touchcancel, removeTouchCancelEvent)
WX_REMOVE_EVENT(accessibilityMagicTap, removeAccessibilityMagicTapEvent)
WX_REMOVE_EVENTS(stoppropagation,stopPropagation, removeStopPropagationEvent)
if(_isListenPseudoTouch) {
self.touchGesture.listenPseudoTouch = NO;
}
[self removeEvent:removeEventName];
}
- (void)_removeAllEvents
{
[self removeClickEvent];
[self removeLongPressEvent];
[self removePanStartEvent];
[self removePanMoveEvent];
[self removePanEndEvent];
[self removeHorizontalPanEvent];
[self removeVerticalPanEvent];
[self removeTouchStartEvent];
[self removeTouchMoveEvent];
[self removeTouchEndEvent];
[self removeTouchCancelEvent];
[self removeSwipeEvent];
[self removePseudoTouchEvent];
}
- (void)_collectSubcomponents:(NSMutableArray *)components
{
for (WXComponent* c in _subcomponents) {
[components addObject:c];
[c _collectSubcomponents:components];
}
}
#pragma mark - Appear & Disappear
- (void)addAppearEvent
{
_appearEvent = YES;
[self.ancestorScroller addScrollToListener:self];
}
- (void)addDisappearEvent
{
_disappearEvent = YES;
[self.ancestorScroller addScrollToListener:self];
}
- (void)removeAppearEvent
{
_appearEvent = NO;
[self.ancestorScroller removeScrollToListener:self];
}
- (void)removeDisappearEvent
{
_disappearEvent = NO;
[self.ancestorScroller removeScrollToListener:self];
}
- (void)removePseudoTouchEvent
{
_touchGesture.listenPseudoTouch = NO;
[self checkRemoveTouchGesture];
}
#pragma mark - Accessibility Event
- (void)addAccessibilityMagicTapEvent
{
_accessibilityMagicTapEvent = YES;
}
- (void)removeAccessibilityMagicTapEvent
{
_accessibilityMagicTapEvent = NO;
}
#pragma mark - StopPropagation
- (void)addStopPropagationEvent:(NSString *)stopPropagationName
{
_listenStopPropagation = YES;
_stopPropagationName = stopPropagationName;
self.touchGesture.listenTouchMove = YES;
}
- (void)removeStopPropagationEvent
{
_listenStopPropagation = NO;
self.touchGesture.listenTouchMove = NO;
}
#pragma mark - Click Event
- (void)addClickEvent
{
if (!_tapGesture) {
_tapGesture = [[UITapGestureRecognizer alloc] initWithTarget:self action:@selector(onClick:)];
_tapGesture.delegate = self;
_tapGesture.cancelsTouchesInView = _cancelsTouchesInView;
[self.view addGestureRecognizer:_tapGesture];
}
}
- (void)removeClickEvent
{
if (_tapGesture) {
_tapGesture.delegate = nil;
if ([self isViewLoaded]) {
if ([self.view.gestureRecognizers containsObject:_tapGesture]) {
[self.view removeGestureRecognizer:_tapGesture];
}
}
@try {
[_tapGesture removeTarget:self action:@selector(onClick:)];
}@catch(NSException *exception) {
WXLog(@"%@", exception);
}
_tapGesture = nil;
}
}
- (void)onClick:(__unused UITapGestureRecognizer *)recognizer
{
NSMutableDictionary *position = [[NSMutableDictionary alloc] initWithCapacity:4];
CGFloat scaleFactor = self.weexInstance.pixelScaleFactor;
if (![self isViewLoaded]) {
return;
}
if (!CGRectEqualToRect(self.view.frame, CGRectZero)) {
CGPoint pageLocation = [recognizer locationInView:self.weexInstance.rootView];
CGRect frame = [self.view.superview convertRect:self.view.frame toView:self.view.window];
position[@"x"] = @(frame.origin.x/scaleFactor);
position[@"y"] = @(frame.origin.y/scaleFactor);
position[@"width"] = @(frame.size.width/scaleFactor);
position[@"height"] = @(frame.size.height/scaleFactor);
position[@"pageX"] = @(pageLocation.x/scaleFactor);
position[@"pageY"] = @(pageLocation.y/scaleFactor);
}
[self fireEvent:@"click" params:@{@"position":position}];
}
#pragma mark - Swipe event
- (void)addSwipeEvent
{
if (_swipeGestures) {
return;
}
_swipeGestures = [NSMutableArray arrayWithCapacity:4];
// It's a little weird because the UISwipeGestureRecognizer.direction property is an options-style bit mask, but each recognizer can only handle one direction
SEL selector = @selector(onSwipe:);
UISwipeGestureRecognizer *upSwipeRecognizer = [[UISwipeGestureRecognizer alloc] initWithTarget:self
action:selector];
upSwipeRecognizer.direction = UISwipeGestureRecognizerDirectionUp;
upSwipeRecognizer.delegate = self;
[_swipeGestures addObject:upSwipeRecognizer];
[self.view addGestureRecognizer:upSwipeRecognizer];
UISwipeGestureRecognizer *downSwipeRecognizer = [[UISwipeGestureRecognizer alloc] initWithTarget:self
action:selector];
downSwipeRecognizer.direction = UISwipeGestureRecognizerDirectionDown;
downSwipeRecognizer.delegate = self;
[_swipeGestures addObject:downSwipeRecognizer];
[self.view addGestureRecognizer:downSwipeRecognizer];
UISwipeGestureRecognizer *rightSwipeRecognizer = [[UISwipeGestureRecognizer alloc] initWithTarget:self
action:selector];
rightSwipeRecognizer.direction = UISwipeGestureRecognizerDirectionRight;
rightSwipeRecognizer.delegate = self;
[_swipeGestures addObject:rightSwipeRecognizer];
[self.view addGestureRecognizer:rightSwipeRecognizer];
UISwipeGestureRecognizer *leftSwipeRecognizer = [[UISwipeGestureRecognizer alloc] initWithTarget:self
action:selector];
leftSwipeRecognizer.direction = UISwipeGestureRecognizerDirectionLeft;
leftSwipeRecognizer.delegate = self;
[_swipeGestures addObject:leftSwipeRecognizer];
[self.view addGestureRecognizer:leftSwipeRecognizer];
}
- (void)removeSwipeEvent
{
if (_swipeGestures == nil) {
return;
}
@try {
for (UISwipeGestureRecognizer *recognizer in _swipeGestures) {
recognizer.delegate = nil;
if([self isViewLoaded]) {
if ([[self.view gestureRecognizers] containsObject:recognizer]) {
[self.view removeGestureRecognizer:recognizer];
}
}
[recognizer removeTarget:self action:@selector(onSwipe:)];
}
}@catch(NSException *exception) {
WXLog(@"%@", exception);
}
_swipeGestures = nil;
}
- (void)onSwipe:(UISwipeGestureRecognizer *)gesture
{
if (![self isViewLoaded]) {
return;
}
UISwipeGestureRecognizerDirection direction = gesture.direction;
NSString *directionString;
switch(direction) {
case UISwipeGestureRecognizerDirectionLeft:
directionString = @"left";
break;
case UISwipeGestureRecognizerDirectionRight:
directionString = @"right";
break;
case UISwipeGestureRecognizerDirectionUp:
directionString = @"up";
break;
case UISwipeGestureRecognizerDirectionDown:
directionString = @"down";
break;
default:
directionString = @"unknown";
break;
}
CGPoint screenLocation = [gesture locationInView:self.view.window];
CGPoint pageLoacation = [gesture locationInView:self.weexInstance.rootView];
NSDictionary *resultTouch = [self touchResultWithScreenLocation:screenLocation pageLocation:pageLoacation identifier:gesture.wx_identifier];
[self fireEvent:@"swipe" params:@{@"direction":directionString, @"changedTouches":resultTouch ? @[resultTouch] : @[]}];
}
#pragma mark - Long Press
- (void)addLongPressEvent
{
if (!_longPressGesture) {
_longPressGesture = [[UILongPressGestureRecognizer alloc] initWithTarget:self action:@selector(onLongPress:)];
_longPressGesture.delegate = self;
[self.view addGestureRecognizer:_longPressGesture];
}
}
- (void)removeLongPressEvent
{
if (_longPressGesture) {
_longPressGesture.delegate = nil;
if ([self isViewLoaded]) {
if([[self.view gestureRecognizers] containsObject:_longPressGesture]) {
[self.view removeGestureRecognizer:_longPressGesture];
}
}
@try {
[_longPressGesture removeTarget:self action:@selector(onLongPress:)];
}@catch(NSException * exception) {
WXLog(@"%@", exception);
}
_longPressGesture = nil;
}
}
- (void)onLongPress:(UILongPressGestureRecognizer *)gesture
{
if (![self isViewLoaded]) {
return;
}
if (gesture.state == UIGestureRecognizerStateBegan) {
CGPoint screenLocation = [gesture locationInView:self.view.window];
CGPoint pageLoacation = [gesture locationInView:self.weexInstance.rootView];
NSDictionary *resultTouch = [self touchResultWithScreenLocation:screenLocation pageLocation:pageLoacation identifier:gesture.wx_identifier];
[self fireEvent:@"longpress" params:@{@"changedTouches":resultTouch ? @[resultTouch] : @[]}];
} else if (gesture.state == UIGestureRecognizerStateEnded) {
gesture.wx_identifier = nil;
}
}
#pragma mark - Pan
- (void)addPanGesture
{
if (!_panGesture) {
_panGesture = [[UIPanGestureRecognizer alloc] initWithTarget:self action:@selector(onPan:)];
_panGesture.delegate = self;
[self.view addGestureRecognizer:_panGesture];
}
}
- (void)addPanStartEvent
{
_listenPanStart = YES;
[self addPanGesture];
}
- (void)addPanMoveEvent
{
_listenPanMove = YES;
[self addPanGesture];
}
- (void)addPanEndEvent
{
_listenPanEnd = YES;
[self addPanGesture];
}
- (void)addHorizontalPanEvent
{
_listenHorizontalPan = YES;
[self addPanGesture];
}
- (void)addVerticalPanEvent
{
_listenVerticalPan = YES;
[self addPanGesture];
}
- (void)onPan:(UIPanGestureRecognizer *)gesture
{
if (![self isViewLoaded]) {
return;
}
CGPoint screenLocation = [gesture locationInView:self.view.window];
CGPoint pageLoacation = [gesture locationInView:self.weexInstance.rootView];
NSString *eventName;
NSString *state = @"";
NSDictionary *resultTouch = [self touchResultWithScreenLocation:screenLocation pageLocation:pageLoacation identifier:gesture.wx_identifier];
if (gesture.state == UIGestureRecognizerStateBegan) {
if (_listenPanStart) {
eventName = @"panstart";
}
state = @"start";
} else if (gesture.state == UIGestureRecognizerStateEnded) {
if (_listenPanEnd) {
eventName = @"panend";
}
state = @"end";
gesture.wx_identifier = nil;
} else if (gesture.state == UIGestureRecognizerStateChanged) {
if (_listenPanMove) {
eventName = @"panmove";
}
state = @"move";
} else if (gesture.state == UIGestureRecognizerStateCancelled) {
state = @"cancel";
}
CGPoint translation = [_panGesture translationInView:self.view];
if (_listenHorizontalPan && (gesture.state != UIGestureRecognizerStateBegan || fabs(translation.y) <= fabs(translation.x))) {
[self fireEvent:@"horizontalpan" params:@{@"state":state, @"changedTouches":resultTouch ? @[resultTouch] : @[]}];
}
if (_listenVerticalPan && (gesture.state != UIGestureRecognizerStateBegan || fabs(translation.y) >= fabs(translation.x))) {
[self fireEvent:@"verticalpan" params:@{@"state":state, @"changedTouches":resultTouch ? @[resultTouch] : @[]}];
}
if (eventName) {
[self fireEvent:eventName params:@{@"changedTouches":resultTouch ? @[resultTouch] : @[]}];
}
}
- (void)removePanStartEvent
{
_listenPanStart = NO;
[self checkRemovePanGesture];
}
- (void)removePanMoveEvent
{
_listenPanMove = NO;
[self checkRemovePanGesture];
}
- (void)removePanEndEvent
{
_listenPanEnd = NO;
[self checkRemovePanGesture];
}
- (void)removeHorizontalPanEvent
{
_listenHorizontalPan = NO;
[self checkRemovePanGesture];
}
- (void)removeVerticalPanEvent
{
_listenVerticalPan = NO;
[self checkRemovePanGesture];
}
- (void)checkRemovePanGesture
{
if (_panGesture
&& !_listenPanStart && !_listenPanMove && !_listenPanEnd
&& !_listenHorizontalPan && !_listenVerticalPan
) {
if ([self isViewLoaded]) {
if ([[self.view gestureRecognizers] containsObject:_panGesture]) {
[self.view removeGestureRecognizer:_panGesture];
}
}
_panGesture.delegate = nil;
@try {
[_panGesture removeTarget:self action:@selector(onPan:)];
}@catch(NSException * exception) {
WXLog(@"%@", exception);
}
_panGesture = nil;
}
}
#pragma mark - Touch Event
- (WXTouchGestureRecognizer *)touchGesture
{
if (!_touchGesture) {
_touchGesture = [[WXTouchGestureRecognizer alloc] initWithComponent:self];
_touchGesture.delegate = self;
[self.view addGestureRecognizer:_touchGesture];
}
return _touchGesture;
}
- (void)addTouchStartEvent
{
self.touchGesture.listenTouchStart = YES;
}
- (void)addTouchMoveEvent
{
self.touchGesture.listenTouchMove = YES;
}
- (void)addTouchEndEvent
{
self.touchGesture.listenTouchEnd = YES;
}
- (void)addTouchCancelEvent
{
self.touchGesture.listenTouchCancel = YES;
}
- (void)removeTouchStartEvent
{
_touchGesture.listenTouchStart = NO;
[self checkRemoveTouchGesture];
}
- (void)removeTouchMoveEvent
{
_touchGesture.listenTouchMove = NO;
[self checkRemoveTouchGesture];
}
- (void)removeTouchEndEvent
{
_touchGesture.listenTouchEnd = NO;
[self checkRemoveTouchGesture];
}
- (void)removeTouchCancelEvent
{
_touchGesture.listenTouchCancel = NO;
[self checkRemoveTouchGesture];
}
- (void)checkRemoveTouchGesture
{
if (_touchGesture && !_touchGesture.listenTouchStart && !_touchGesture.listenTouchMove && !_touchGesture.listenTouchEnd && !_touchGesture.listenTouchCancel && !_touchGesture.listenPseudoTouch) {
_touchGesture.delegate = nil;
if ([self isViewLoaded]) {
if ([[self.view gestureRecognizers] containsObject:_touchGesture]) {
[self.view removeGestureRecognizer:_touchGesture];
}
}
_touchGesture = nil;
}
}
- (BOOL)gestureShouldStopPropagation:(UIGestureRecognizer *)gestureRecognizer shouldReceiveTouch:(UITouch *)touch
{
if(touch.wx_stopPropagation && [touch.wx_stopPropagation isEqualToNumber:@1]){
return NO;
}
else
{
if (_listenStopPropagation)
{
NSString *ref = _templateComponent ? _templateComponent.ref : self.ref;
CGPoint screenLocation = [touch locationInView:touch.window];
CGPoint pageLocation = [touch locationInView:self.weexInstance.rootView];
NSDictionary *resultTouch = [self touchResultWithScreenLocation:screenLocation pageLocation:pageLocation identifier:touch.wx_identifier];
NSString *touchState;
if (touch.phase == UITouchPhaseBegan) {
touchState = @"start";
}
else if (touch.phase == UITouchPhaseMoved){
touchState = @"move";
}
else{
touchState = @"end";
}
BOOL stopPropagation = [[WXEventManager sharedManager]stopPropagation:self.weexInstance.instanceId ref:ref type:_stopPropagationName params:@{@"changedTouches":resultTouch ? @[resultTouch] : @[],@"action":touchState}];
touch.wx_stopPropagation = stopPropagation ? @1 : @0;
//only custom event on custom component will make not receive touch
//you can use custom-event="yes" to enable this feature
return _customEvent ? !stopPropagation : YES;
}
}
return YES;
}
#pragma mark - UIGestureRecognizerDelegate
- (BOOL)gestureRecognizer:(UIGestureRecognizer *)gestureRecognizer shouldReceiveTouch:(UITouch *)touch
{
return [self gestureShouldStopPropagation:gestureRecognizer shouldReceiveTouch:touch];
}
- (BOOL)gestureRecognizerShouldBegin:(UIGestureRecognizer *)gestureRecognizer
{
if (gestureRecognizer == _panGesture) {
CGPoint translation = [_panGesture translationInView:self.view];
if (_listenHorizontalPan && !_listenVerticalPan && fabs(translation.y) > fabs(translation.x)) {
return NO;
}
}
return YES;
}
- (BOOL)gestureRecognizer:(UIGestureRecognizer *)gestureRecognizer shouldRecognizeSimultaneouslyWithGestureRecognizer:(UIGestureRecognizer *)otherGestureRecognizer
{
NSString * panGestureRecog = [NSString stringWithFormat:@"%@%@%@%@%@%@",@"UIScrollV", @"iewPanG", @"estur",@"eRecog",@"nize",@"r"];
NSString * textTap = [NSString stringWithFormat:@"%@%@%@%@%@",@"UITe",@"xtTa",@"pReco",@"gniz",@"er"];
// trigger touches
if ([gestureRecognizer isKindOfClass:[WXTouchGestureRecognizer class]]) {
return YES;
}
// swipe and scroll
if ([gestureRecognizer isKindOfClass:[UISwipeGestureRecognizer class]] && [otherGestureRecognizer isKindOfClass:NSClassFromString(panGestureRecog)]) {
return YES;
}
// onclick and textviewInput
if ([gestureRecognizer isKindOfClass:[UITapGestureRecognizer class]] && [otherGestureRecognizer isKindOfClass: NSClassFromString(textTap)]) {
return YES;
}
return NO;
}
- (BOOL)gestureRecognizer:(UIGestureRecognizer *)gestureRecognizer shouldRequireFailureOfGestureRecognizer:(UIGestureRecognizer *)otherGestureRecognizer
{
if ([gestureRecognizer isKindOfClass:[UITapGestureRecognizer class]] &&
[otherGestureRecognizer isKindOfClass:[UIPanGestureRecognizer class]]) {
if (otherGestureRecognizer.state != UIGestureRecognizerStateFailed) {
if ([gestureRecognizer view].wx_component != nil && [otherGestureRecognizer view].wx_component != nil) {
return YES;
}
}
}
return NO;
}
#pragma mark - Utils
- (NSDictionary *)touchResultWithScreenLocation:(CGPoint)screenLocation pageLocation:(CGPoint)pageLocation identifier:(NSNumber *)identifier
{
NSMutableDictionary *resultTouch = [[NSMutableDictionary alloc] initWithCapacity:5];
CGFloat scaleFactor = self.weexInstance.pixelScaleFactor;
resultTouch[@"screenX"] = @(screenLocation.x/scaleFactor);
resultTouch[@"screenY"] = @(screenLocation.y/scaleFactor);
resultTouch[@"pageX"] = @(pageLocation.x/scaleFactor);
resultTouch[@"pageY"] = @(pageLocation.y/scaleFactor);
resultTouch[@"identifier"] = identifier;
return resultTouch;
}
// find virtual component's root component
- (WXComponent*)getRecycleListComponent
{
if ([self isKindOfClass:[WXRecycleListComponent class]]) {
return self;
}
if ([self.ref isEqualToString:WX_SDK_ROOT_REF]) {
return nil;
}
return [self.supercomponent getRecycleListComponent];
}
@end
@implementation WXTouchGestureRecognizer
{
__weak WXComponent *_component;
NSUInteger _touchIdentifier;
}
- (instancetype)initWithTarget:(id)target action:(SEL)action
{
return [self initWithComponent:nil];
}
- (instancetype)initWithComponent:(WXComponent *)component
{
if (self = [super initWithTarget:self action:@selector(touchResponse:)]) {
_component = component;
_listenTouchStart = NO;
_listenTouchEnd = NO;
_listenTouchMove = NO;
_listenTouchCancel = NO;
self.cancelsTouchesInView = NO;
}
return self;
}
- (void)touchesBegan:(NSSet<UITouch *> *)touches withEvent:(UIEvent *)event
{
[super touchesBegan:touches withEvent:event];
if (_listenTouchStart) {
[self fireTouchEvent:@"touchstart" withTouches:touches];
}
if(_listenPseudoTouch) {
NSMutableDictionary *styles = [_component getPseudoClassStyles:@"active"];
[_component updatePseudoClassStyles:styles];
}
}
- (void)touchesMoved:(NSSet<UITouch *> *)touches withEvent:(UIEvent *)event
{
[super touchesMoved:touches withEvent:event];
if (_listenTouchMove) {
[self fireTouchEvent:@"touchmove" withTouches:touches];
}
}
- (void)touchesEnded:(NSSet<UITouch *> *)touches withEvent:(UIEvent *)event
{
[super touchesEnded:touches withEvent:event];
if (_listenTouchEnd) {
[self fireTouchEvent:@"touchend" withTouches:touches];
}
if(_listenPseudoTouch) {
[self recoveryPseudoStyles:_component.styles];
}
}
- (void)touchesCancelled:(NSSet<UITouch *> *)touches withEvent:(UIEvent *)event
{
[super touchesCancelled:touches withEvent:event];
if (_listenTouchCancel) {
[self fireTouchEvent:@"touchcancel" withTouches:touches];
}
if(_listenPseudoTouch) {
[self recoveryPseudoStyles:_component.styles];
}
}
- (void)fireTouchEvent:(NSString *)eventName withTouches:(NSSet<UITouch *> *)touches
{
if (_component == nil) {
return;
}
NSMutableArray *resultTouches = [NSMutableArray new];
CGPoint accmOffset = CGPointZero;
UIView* rootView = _component.weexInstance.rootView;
// UIView* view = self.view;
// while (view && view != rootView) {
// if ([view isKindOfClass:[UIScrollView class]]) {
// CGPoint offset = ((UIScrollView*)view).contentOffset;
// accmOffset.x += offset.x;
// accmOffset.y += offset.y;
// }
// view = view.superview;
// }
for (UITouch *touch in touches) {
CGPoint screenLocation = [touch locationInView:touch.window];
CGPoint pageLocation = [touch locationInView:rootView];
pageLocation.x += accmOffset.x;
pageLocation.y += accmOffset.y;
if (!touch.wx_identifier) {
touch.wx_identifier = @(_touchIdentifier++);
}
NSDictionary *resultTouch = [_component touchResultWithScreenLocation:screenLocation pageLocation:pageLocation identifier:touch.wx_identifier];
NSMutableDictionary * mutableResultTouch = [resultTouch mutableCopy];
float value = touch.force*60;
float maxValue = touch.maximumPossibleForce*60;
if (touch.maximumPossibleForce) {
// the forece value will be range 1 from 0.
[mutableResultTouch setObject:[NSNumber numberWithFloat:value/maxValue] forKey:@"force"];
}else {
[mutableResultTouch setObject:[NSNumber numberWithFloat:0.0] forKey:@"force"];
}
if (mutableResultTouch) { // component is nil, mutableResultTouch will be nil
[resultTouches addObject:mutableResultTouch];
}
}
[_component fireEvent:eventName params:@{@"changedTouches":resultTouches ?: @[]}];
}
- (void)recoveryPseudoStyles:(NSDictionary *)styles
{
[_component recoveryPseudoStyles:styles];
}
- (void)touchResponse:(UIGestureRecognizer *)gesture
{
}
@end