http://git-wip-us.apache.org/repos/asf/incubator-weex/blob/b77b4259/ios/sdk/WeexSDK/Sources/Manager/WXComponentManager.mm ---------------------------------------------------------------------- diff --git a/ios/sdk/WeexSDK/Sources/Manager/WXComponentManager.mm b/ios/sdk/WeexSDK/Sources/Manager/WXComponentManager.mm new file mode 100644 index 0000000..c8c6e3f --- /dev/null +++ b/ios/sdk/WeexSDK/Sources/Manager/WXComponentManager.mm @@ -0,0 +1,1158 @@ +/* + * 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 "WXComponentManager.h" +#import "WXComponent.h" +#import "WXComponent_internal.h" +#import "WXComponent+DataBinding.h" +#import "WXComponentFactory.h" +#import "WXDefine.h" +#import "NSArray+Weex.h" +#import "WXSDKInstance.h" +#import "WXAssert.h" +#import "WXUtility.h" +#import "WXMonitor.h" +#import "WXScrollerProtocol.h" +#import "WXSDKManager.h" +#import "WXSDKError.h" +#import "WXInvocationConfig.h" +#import "WXHandlerFactory.h" +#import "WXValidateProtocol.h" +#import "WXPrerenderManager.h" +#import "WXTracingManager.h" +#import "WXLayoutDefine.h" +#import "WXSDKInstance_performance.h" +#import "WXRootView.h" +#import "WXComponent+Layout.h" + + + +static NSThread *WXComponentThread; + +#define WXAssertComponentExist(component) WXAssert(component, @"component not exists") + + +@implementation WXComponentManager +{ + __weak WXSDKInstance *_weexInstance; + BOOL _isValid; + + BOOL _stopRunning; + NSUInteger _noTaskTickCount; + + // access only on component thread + NSMapTable<NSString *, WXComponent *> *_indexDict; + NSMutableArray<dispatch_block_t> *_uiTaskQueue; + NSMutableDictionary *_uiPrerenderTaskQueue; + + WXComponent *_rootComponent; + NSMutableArray *_fixedComponents; +//#ifndef USE_FLEX + css_node_t *_rootCSSNode; +//#else + WeexCore::WXCoreLayoutNode* _rootFlexCSSNode; +//#endif + CADisplayLink *_displayLink; +} + ++ (instancetype)sharedManager +{ + static id _sharedInstance = nil; + static dispatch_once_t oncePredicate; + dispatch_once(&oncePredicate, ^{ + _sharedInstance = [[self alloc] init]; + }); + return _sharedInstance; +} + +- (instancetype)initWithWeexInstance:(id)weexInstance +{ + if (self = [self init]) { + _weexInstance = weexInstance; + + _indexDict = [NSMapTable strongToWeakObjectsMapTable]; + _fixedComponents = [NSMutableArray wx_mutableArrayUsingWeakReferences]; + _uiTaskQueue = [NSMutableArray array]; + _isValid = YES; + [self _startDisplayLink]; + } + + return self; +} + +- (void)dealloc +{ +//#ifndef USE_FLEX + if(![WXComponent isUseFlex]) + { + free_css_node(_rootCSSNode); + } + +//#else + + if(_rootFlexCSSNode){ + delete _rootFlexCSSNode; + + // WeexCore::WXCoreLayoutNode::freeNodeTree(_rootFlexCSSNode); + _rootFlexCSSNode=nullptr; + } +//#endif + [NSMutableArray wx_releaseArray:_fixedComponents]; +} + +#pragma mark Thread Management + ++ (NSThread *)componentThread +{ + static dispatch_once_t onceToken; + dispatch_once(&onceToken, ^{ + WXComponentThread = [[NSThread alloc] initWithTarget:[self sharedManager] selector:@selector(_runLoopThread) object:nil]; + [WXComponentThread setName:WX_COMPONENT_THREAD_NAME]; + if(WX_SYS_VERSION_GREATER_THAN_OR_EQUAL_TO(@"8.0")) { + [WXComponentThread setQualityOfService:[[NSThread mainThread] qualityOfService]]; + } else { + [WXComponentThread setThreadPriority:[[NSThread mainThread] threadPriority]]; + } + + [WXComponentThread start]; + }); + + return WXComponentThread; +} + +- (void)_runLoopThread +{ + [[NSRunLoop currentRunLoop] addPort:[NSMachPort port] forMode:NSDefaultRunLoopMode]; + + while (!_stopRunning) { + @autoreleasepool { + [[NSRunLoop currentRunLoop] runMode:NSDefaultRunLoopMode beforeDate:[NSDate distantFuture]]; + } + } +} + ++ (void)_performBlockOnComponentThread:(void (^)(void))block +{ + if([NSThread currentThread] == [self componentThread]){ + block(); + } else { + [self performSelector:@selector(_performBlockOnComponentThread:) + onThread:WXComponentThread + withObject:[block copy] + waitUntilDone:NO]; + } +} + ++ (void)_performBlockSyncOnComponentThread:(void (^)(void))block +{ + if([NSThread currentThread] == [self componentThread]){ + block(); + } else { + [self performSelector:@selector(_performBlockOnComponentThread:) + onThread:WXComponentThread + withObject:[block copy] + waitUntilDone:YES]; + } +} + +- (void)startComponentTasks +{ + [self _awakeDisplayLink]; +} + +- (void)rootViewFrameDidChange:(CGRect)frame +{ + WXAssertComponentThread(); +//#ifndef USE_FLEX + + if (![WXComponent isUseFlex]) { + if (_rootCSSNode) { + [self _applyRootFrame:frame toRootCSSNode:_rootCSSNode]; + if (!_rootComponent.styles[@"width"]) { + _rootComponent.cssNode->style.dimensions[CSS_WIDTH] = frame.size.width ?: CSS_UNDEFINED; + } + if (!_rootComponent.styles[@"height"]) { + _rootComponent.cssNode->style.dimensions[CSS_HEIGHT] = frame.size.height ?: CSS_UNDEFINED; + } + } + } +//#else + else + { + if (_rootFlexCSSNode) { + [self _applyRootFrame:frame]; + if (!_rootComponent.styles[@"width"]) { + _rootComponent.flexCssNode->setStyleWidth(frame.size.width ?:FlexUndefined,NO); + } + if (!_rootComponent.styles[@"height"]) { + _rootComponent.flexCssNode->setStyleHeight(frame.size.height ?:FlexUndefined); + } + } + } +//#endif + [_rootComponent setNeedsLayout]; + [self startComponentTasks]; +} + +//#ifndef USE_FLEX +- (void)_applyRootFrame:(CGRect)rootFrame toRootCSSNode:(css_node_t *)rootCSSNode +{ + _rootCSSNode->style.position[CSS_LEFT] = self.weexInstance.frame.origin.x; + _rootCSSNode->style.position[CSS_TOP] = self.weexInstance.frame.origin.y; + // if no instance width/height, use layout width/height, as Android's wrap_content + _rootCSSNode->style.dimensions[CSS_WIDTH] = self.weexInstance.frame.size.width ?: CSS_UNDEFINED; + _rootCSSNode->style.dimensions[CSS_HEIGHT] = self.weexInstance.frame.size.height ?: CSS_UNDEFINED; +} +//#else +- (void)_applyRootFrame:(CGRect)rootFrame{ + _rootFlexCSSNode->setStylePosition(WeexCore::kPositionEdgeLeft, self.weexInstance.frame.origin.x); + _rootFlexCSSNode->setStylePosition(WeexCore::kPositionEdgeTop, self.weexInstance.frame.origin.y); + _rootFlexCSSNode->setStyleWidth(self.weexInstance.frame.size.width ?: FlexUndefined,NO); + _rootFlexCSSNode->setStyleHeight(self.weexInstance.frame.size.height ?: FlexUndefined); +} +//#endif + +- (void)_addUITask:(void (^)(void))block +{ + if(!_uiPrerenderTaskQueue){ + _uiPrerenderTaskQueue = [NSMutableDictionary new]; + } + if(self.weexInstance.needPrerender){ + NSMutableArray<dispatch_block_t> *tasks = [_uiPrerenderTaskQueue objectForKey:[WXPrerenderManager getTaskKeyFromUrl:self.weexInstance.scriptURL.absoluteString]]; + if(!tasks){ + tasks = [NSMutableArray new]; + } + [tasks addObject:block]; + [_uiPrerenderTaskQueue setObject:tasks forKey:[WXPrerenderManager getTaskKeyFromUrl:self.weexInstance.scriptURL.absoluteString]]; + }else{ + [_uiTaskQueue addObject:block]; + } +} + +- (void)excutePrerenderUITask:(NSString *)url +{ + NSMutableArray *tasks = [_uiPrerenderTaskQueue objectForKey:[WXPrerenderManager getTaskKeyFromUrl:self.weexInstance.scriptURL.absoluteString]]; + for (id block in tasks) { + [_uiTaskQueue addObject:block]; + } + tasks = [NSMutableArray new]; + [_uiPrerenderTaskQueue setObject:tasks forKey:[WXPrerenderManager getTaskKeyFromUrl:self.weexInstance.scriptURL.absoluteString]]; +} + +#pragma mark Component Tree Building + +- (void)createRoot:(NSDictionary *)data +{ + WXAssertComponentThread(); + WXAssertParam(data); + + _rootComponent = [self _buildComponentForData:data supercomponent:nil]; +//#ifndef USE_FLEX + if(![WXComponent isUseFlex]) + { + [self _initRootCSSNode]; + } +//#else + else + { + [self _initRootFlexCssNode]; + _rootFlexCSSNode->addChildAt(_rootComponent.flexCssNode, (uint32_t)[_fixedComponents count]); + } +//#endif + + NSArray *subcomponentsData = [data valueForKey:@"children"]; + if (subcomponentsData) { + BOOL appendTree = [_rootComponent.attributes[@"append"] isEqualToString:@"tree"]; + for(NSDictionary *subcomponentData in subcomponentsData){ + [self _recursivelyAddComponent:subcomponentData toSupercomponent:_rootComponent atIndex:-1 appendingInTree:appendTree]; + } + } + + __weak typeof(self) weakSelf = self; + WX_MONITOR_INSTANCE_PERF_END(WXFirstScreenJSFExecuteTime, self.weexInstance); + [self _addUITask:^{ + [WXTracingManager startTracingWithInstanceId:weakSelf.weexInstance.instanceId ref:data[@"ref"] className:nil name:data[@"type"] phase:WXTracingBegin functionName:@"createBody" options:@{@"threadName":WXTUIThread}]; + __strong typeof(self) strongSelf = weakSelf; + strongSelf.weexInstance.rootView.wx_component = strongSelf->_rootComponent; + [strongSelf.weexInstance.rootView addSubview:strongSelf->_rootComponent.view]; + [WXTracingManager startTracingWithInstanceId:weakSelf.weexInstance.instanceId ref:data[@"ref"] className:nil name:data[@"type"] phase:WXTracingEnd functionName:@"createBody" options:@{@"threadName":WXTUIThread}]; + }]; + + +} + +//#ifndef USE_FLEX +static bool rootNodeIsDirty(void *context) +{ + WXComponentManager *manager = (__bridge WXComponentManager *)(context); + return [manager->_rootComponent needsLayout]; +} + +static css_node_t * rootNodeGetChild(void *context, int i) +{ + WXComponentManager *manager = (__bridge WXComponentManager *)(context); + if (i == 0) { + return manager->_rootComponent.cssNode; + } else if(manager->_fixedComponents.count >= i) { + return ((WXComponent *)((manager->_fixedComponents)[i-1])).cssNode; + } + + return NULL; +} +//#endif + +- (void)addComponent:(NSDictionary *)componentData toSupercomponent:(NSString *)superRef atIndex:(NSInteger)index appendingInTree:(BOOL)appendingInTree +{ + WXAssertComponentThread(); + WXAssertParam(componentData); + WXAssertParam(superRef); + + WXComponent *supercomponent = [_indexDict objectForKey:superRef]; + WXAssertComponentExist(supercomponent); + + if ([WXComponent isUseFlex] && !supercomponent) { + WXLogWarning(@"addComponent,superRef from js never exit ! check JS action, supRef:%@",superRef); + return; + } + + [self _recursivelyAddComponent:componentData toSupercomponent:supercomponent atIndex:index appendingInTree:appendingInTree]; +} + +- (void)_recursivelyAddComponent:(NSDictionary *)componentData toSupercomponent:(WXComponent *)supercomponent atIndex:(NSInteger)index appendingInTree:(BOOL)appendingInTree +{ + WXComponent *component = [self _buildComponentForData:componentData supercomponent:supercomponent]; + if (!supercomponent.subcomponents) { + index = 0; + } else { + index = (index == -1 ? supercomponent->_subcomponents.count : index); + } + +#ifdef DEBUG +//#ifndef USE_FLEX + if(![WXComponent isUseFlex]) + { + WXLogDebug(@"flexLayout -> _recursivelyAddComponent : super:(%@,%@):[%f,%f] ,child:(%@,%@):[%f,%f],childClass:%@", + supercomponent.type, + supercomponent.ref, + supercomponent.cssNode->style.dimensions[CSS_WIDTH], + supercomponent.cssNode->style.dimensions[CSS_HEIGHT], + component.type, + component.ref, + component.cssNode->style.dimensions[CSS_WIDTH], + component.cssNode->style.dimensions[CSS_HEIGHT] + ,NSStringFromClass([component class]) + ); + } +//#else + else + { + WXLogDebug(@"flexLayout -> _recursivelyAddComponent : super:(%@,%@):[%f,%f] ,child:(%@,%@):[%f,%f],childClass:%@", + supercomponent.type, + supercomponent.ref, + supercomponent.flexCssNode->getStyleWidth(), + supercomponent.flexCssNode->getStyleHeight(), + component.type, + component.ref, + component.flexCssNode->getStyleWidth(), + component.flexCssNode->getStyleHeight() + ,NSStringFromClass([component class]) + ); + } +//#endif +#endif //DEBUG + + + [supercomponent _insertSubcomponent:component atIndex:index]; + // use _lazyCreateView to forbid component like cell's view creating + if(supercomponent && component && supercomponent->_lazyCreateView) { + component->_lazyCreateView = YES; + } + + [self recordMaximumVirtualDom:component]; + + if (!component->_isTemplate) { + __weak typeof(self) weakSelf = self; + BOOL isFSCreateFinish = [self weexInstance].isJSCreateFinish; + [self _addUITask:^{ + [WXTracingManager startTracingWithInstanceId:weakSelf.weexInstance.instanceId ref:componentData[@"ref"] className:nil name:componentData[@"type"] phase:WXTracingBegin functionName:@"addElement" options:@{@"threadName":WXTUIThread}]; + [supercomponent insertSubview:component atIndex:index]; + [WXTracingManager startTracingWithInstanceId:weakSelf.weexInstance.instanceId ref:componentData[@"ref"] className:nil name:componentData[@"type"] phase:WXTracingEnd functionName:@"addElement" options:@{@"threadName":WXTUIThread}]; + [weakSelf onElementChange:isFSCreateFinish]; + }]; + } + + NSArray *subcomponentsData = [componentData valueForKey:@"children"]; + + BOOL appendTree = !appendingInTree && [component.attributes[@"append"] isEqualToString:@"tree"]; + // if ancestor is appending tree, child should not be laid out again even it is appending tree. + for(NSDictionary *subcomponentData in subcomponentsData){ + [self _recursivelyAddComponent:subcomponentData toSupercomponent:component atIndex:-1 appendingInTree:appendTree || appendingInTree]; + } + + [component _didInserted]; + + if (appendTree) { + // If appending treeï¼force layout in case of too much tasks piling up in syncQueue + [self _layoutAndSyncUI]; + } +} + +- (void)moveComponent:(NSString *)ref toSuper:(NSString *)superRef atIndex:(NSInteger)index +{ + WXAssertComponentThread(); + WXAssertParam(ref); + WXAssertParam(superRef); + + WXComponent *component = [_indexDict objectForKey:ref]; + WXComponent *newSupercomponent = [_indexDict objectForKey:superRef]; + WXAssertComponentExist(component); + WXAssertComponentExist(newSupercomponent); + + if (component.supercomponent == newSupercomponent && [newSupercomponent.subcomponents indexOfObject:component] < index) { + // if the supercomponent moved to is the same as original supercomponent, + // unify it into the index after removing. + index--; + } + + [component _moveToSupercomponent:newSupercomponent atIndex:index]; + __weak typeof(self) weakSelf = self; + [self _addUITask:^{ + [WXTracingManager startTracingWithInstanceId:weakSelf.weexInstance.instanceId ref:ref className:nil name:nil phase:WXTracingBegin functionName:@"addElement" options:@{@"threadName":WXTUIThread}]; + [component moveToSuperview:newSupercomponent atIndex:index]; + [WXTracingManager startTracingWithInstanceId:weakSelf.weexInstance.instanceId ref:ref className:nil name:nil phase:WXTracingEnd functionName:@"addElement" options:@{@"threadName":WXTUIThread}]; + }]; +} + +- (void)removeComponent:(NSString *)ref +{ + WXAssertComponentThread(); + WXAssertParam(ref); + + WXComponent *component = [_indexDict objectForKey:ref]; + WXAssertComponentExist(component); + + if ([WXComponent isUseFlex] && !component) { + WXLogWarning(@"removeComponent ref from js never exit ! check JS action, ref :%@",ref); + return; + } + + [component _removeFromSupercomponent]; + + [_indexDict removeObjectForKey:ref]; + + __weak typeof(self) weakSelf = self; + BOOL isFSCreateFinish = [self weexInstance].isJSCreateFinish; + [self _addUITask:^{ + [WXTracingManager startTracingWithInstanceId:weakSelf.weexInstance.instanceId ref:ref className:nil name:nil phase:WXTracingBegin functionName:@"removeElement" options:@{@"threadName":WXTUIThread}]; + if (component.supercomponent) { + [component.supercomponent willRemoveSubview:component]; + } + [component removeFromSuperview]; + [WXTracingManager startTracingWithInstanceId:weakSelf.weexInstance.instanceId ref:ref className:nil name:nil phase:WXTracingEnd functionName:@"removeElement" options:@{@"threadName":WXTUIThread}]; + [weakSelf onElementChange:isFSCreateFinish]; + }]; + + [self _checkFixedSubcomponentToRemove:component]; + +} + +- (void)onElementChange:(BOOL)isFSCreateFinish +{ + if (!isFSCreateFinish) { + return; + } + + UIView *root = [self weexInstance].rootView; + BOOL hasEvent = TRUE; + if (root && [root isKindOfClass:[WXRootView class]]) { + WXRootView* wxRootView = (WXRootView *)root; + hasEvent = [wxRootView isHasEvent]; + } + if (hasEvent) { + return; + } + double current = CACurrentMediaTime()*1000; + + double diff = current - [self weexInstance].performance.jsCreateFinishTime; + if (diff > 8000) { + return; + } + [self weexInstance].performance.interactionTime = current - self.weexInstance.performance.renderTimeOrigin; +} + +- (void)recordMaximumVirtualDom:(WXComponent*) component +{ + WXAssertComponentExist(component); + if(!component){ + return; + } + int maxDeep =0; + while (component) { + maxDeep++; + component = component.supercomponent; + } + if(maxDeep > [self weexInstance].performance.maxVdomDeep) + { + [self weexInstance].performance.maxVdomDeep = maxDeep; + } + +} + +- (void)_checkFixedSubcomponentToRemove:(WXComponent *)component +{ + for (WXComponent *subcomponent in component.subcomponents) { + if (subcomponent->_positionType == WXPositionTypeFixed) { + [self _addUITask:^{ + [subcomponent removeFromSuperview]; + }]; + } + + [self _checkFixedSubcomponentToRemove:subcomponent]; + } +} + +- (WXComponent *)componentForRef:(NSString *)ref +{ + WXAssertComponentThread(); + + return [_indexDict objectForKey:ref]; +} + +- (WXComponent *)componentForRoot +{ + return _rootComponent; +} + +- (NSUInteger)numberOfComponents +{ + WXAssertComponentThread(); + + return _indexDict.count; +} + +- (WXComponent *)_buildComponentForData:(NSDictionary *)data supercomponent:(WXComponent *)supercomponent +{ + NSString *ref = data[@"ref"]; + NSString *type = data[@"type"]; + NSDictionary *styles = data[@"style"]; + NSDictionary *attributes = data[@"attr"]; + NSArray *events = data[@"event"]; + + if (self.weexInstance.needValidate) { + id<WXValidateProtocol> validateHandler = [WXHandlerFactory handlerForProtocol:@protocol(WXValidateProtocol)]; + if (validateHandler) { + WXComponentValidateResult* validateResult; + if ([validateHandler respondsToSelector:@selector(validateWithWXSDKInstance:component:supercomponent:)]) { + validateResult = [validateHandler validateWithWXSDKInstance:self.weexInstance component:type supercomponent:supercomponent]; + } + if (validateResult==nil || !validateResult.isSuccess) { + type = validateResult.replacedComponent? validateResult.replacedComponent : @"div"; + WXLogError(@"%@",[validateResult.error.userInfo objectForKey:@"errorMsg"]); + } + } + } + + WXComponentConfig *config = [WXComponentFactory configWithComponentName:type]; + BOOL isTemplate = [config.properties[@"isTemplate"] boolValue] || (supercomponent && supercomponent->_isTemplate); + NSDictionary *bindingStyles; + NSDictionary *bindingAttibutes; + NSDictionary *bindingEvents; + NSDictionary *bindingProps; + if (isTemplate) { + bindingProps = [self _extractBindingProps:&attributes]; + bindingStyles = [self _extractBindings:&styles]; + bindingAttibutes = [self _extractBindings:&attributes]; + bindingEvents = [self _extractBindingEvents:&events]; + } + + Class clazz = NSClassFromString(config.clazz);; + WXComponent *component = [[clazz alloc] initWithRef:ref type:type styles:styles attributes:attributes events:events weexInstance:self.weexInstance]; + if (isTemplate) { + component->_isTemplate = YES; + [component _storeBindingsWithProps:bindingProps styles:bindingStyles attributes:bindingAttibutes events:bindingEvents]; + } + + WXAssert(component, @"Component build failed for data:%@", data); + + [_indexDict setObject:component forKey:component.ref]; + [component readyToRender];// notify redyToRender event when init + return component; +} + +- (void)addComponent:(WXComponent *)component toIndexDictForRef:(NSString *)ref +{ + [_indexDict setObject:component forKey:ref]; +} + +- (NSDictionary *)_extractBindings:(NSDictionary **)attributesOrStylesPoint +{ + NSDictionary *attributesOrStyles = *attributesOrStylesPoint; + if (!attributesOrStyles) { + return nil; + } + + NSMutableDictionary *newAttributesOrStyles = [attributesOrStyles mutableCopy]; + NSMutableDictionary *bindingAttributesOrStyles = [NSMutableDictionary dictionary]; + + [attributesOrStyles enumerateKeysAndObjectsUsingBlock:^(id _Nonnull attributeOrStyleName, id _Nonnull attributeOrStyle, BOOL * _Nonnull stop) { + if ([WXBindingMatchIdentify isEqualToString:attributeOrStyleName] // match + || [WXBindingRepeatIdentify isEqualToString:attributeOrStyleName] // repeat + || [WXBindingOnceIdentify isEqualToString:attributeOrStyleName] // once + ||([attributeOrStyle isKindOfClass:[NSDictionary class]] && attributeOrStyle[WXBindingIdentify])) { // {"attributeOrStyleName": {"@binding":"bindingExpression"} + bindingAttributesOrStyles[attributeOrStyleName] = attributeOrStyle; + [newAttributesOrStyles removeObjectForKey:attributeOrStyleName]; + } else if ([attributeOrStyle isKindOfClass:[NSArray class]]) { + // {"attributeOrStyleName":[..., "string", {"@binding":"bindingExpression"}, "string", {"@binding":"bindingExpression"}, ...] + __block BOOL isBinding = NO; + [attributeOrStyle enumerateObjectsUsingBlock:^(id _Nonnull obj, NSUInteger idx, BOOL * _Nonnull stop) { + if ([obj isKindOfClass:[NSDictionary class]] && obj[WXBindingIdentify]) { + isBinding = YES; + *stop = YES; + } + }]; + + if (isBinding) { + bindingAttributesOrStyles[attributeOrStyleName] = attributeOrStyle; + [newAttributesOrStyles removeObjectForKey:attributeOrStyleName]; + } + } + }]; + + *attributesOrStylesPoint = newAttributesOrStyles; + + return bindingAttributesOrStyles; +} + +- (NSDictionary *)_extractBindingEvents:(NSArray **)eventsPoint +{ + NSArray *events = *eventsPoint; + NSMutableArray *newEvents = [events mutableCopy]; + NSMutableDictionary *bindingEvents = [NSMutableDictionary dictionary]; + [events enumerateObjectsUsingBlock:^(id _Nonnull event, NSUInteger idx, BOOL * _Nonnull stop) { + if ([event isKindOfClass:[NSDictionary class]] && event[@"type"] && event[@"params"]) { + NSString *eventName = event[@"type"]; + NSString *bindingParams = event[@"params"]; + bindingEvents[eventName] = bindingParams; + newEvents[idx] = eventName; + } + }]; + + *eventsPoint = newEvents; + return bindingEvents; +} + +- (NSDictionary *)_extractBindingProps:(NSDictionary **)attributesPoint +{ + NSDictionary *attributes = *attributesPoint; + if (attributes[@"@componentProps"]) { + NSMutableDictionary *newAttributes = [attributes mutableCopy]; + [newAttributes removeObjectForKey:@"@componentProps"]; + *attributesPoint = newAttributes; + return attributes[@"@componentProps"]; + } + + return nil; +} + +#pragma mark Reset +-(BOOL)isShouldReset:(id )value +{ + if([value isKindOfClass:[NSString class]]) { + if(!value || [@"" isEqualToString:value]) { + return YES; + } + } + return NO; +} + +-(void)filterStyles:(NSDictionary *)styles normalStyles:(NSMutableDictionary *)normalStyles resetStyles:(NSMutableArray *)resetStyles +{ + for (NSString *key in styles) { + id value = [styles objectForKey:key]; + if([self isShouldReset:value]) { + [resetStyles addObject:key]; + }else{ + [normalStyles setObject:styles[key] forKey:key]; + } + } +} + +- (void)updateStyles:(NSDictionary *)styles forComponent:(NSString *)ref +{ + [self handleStyles:styles forComponent:ref isUpdateStyles:YES]; +} + +- (void)updatePseudoClassStyles:(NSDictionary *)styles forComponent:(NSString *)ref +{ + [self handleStyles:styles forComponent:ref isUpdateStyles:NO]; +} + +- (void)handleStyleOnMainThread:(NSDictionary*)styles forComponent:(WXComponent *)component isUpdateStyles:(BOOL)isUpdateStyles +{ + WXAssertParam(styles); + WXAssertParam(component); + WXAssertMainThread(); + + NSMutableDictionary *normalStyles = [NSMutableDictionary new]; + NSMutableArray *resetStyles = [NSMutableArray new]; + [self filterStyles:styles normalStyles:normalStyles resetStyles:resetStyles]; + [component _updateStylesOnMainThread:normalStyles resetStyles:resetStyles]; + [component readyToRender]; + + WXPerformBlockOnComponentThread(^{ + [component _updateStylesOnComponentThread:normalStyles resetStyles:resetStyles isUpdateStyles:isUpdateStyles]; + }); +} + +- (void)handleStyles:(NSDictionary *)styles forComponent:(NSString *)ref isUpdateStyles:(BOOL)isUpdateStyles +{ + WXAssertParam(styles); + WXAssertParam(ref); + + WXComponent *component = [_indexDict objectForKey:ref]; + WXAssertComponentExist(component); + + NSMutableDictionary *normalStyles = [NSMutableDictionary new]; + NSMutableArray *resetStyles = [NSMutableArray new]; + [self filterStyles:styles normalStyles:normalStyles resetStyles:resetStyles]; + [component _updateStylesOnComponentThread:normalStyles resetStyles:resetStyles isUpdateStyles:isUpdateStyles]; + [self _addUITask:^{ + [component _updateStylesOnMainThread:normalStyles resetStyles:resetStyles]; + [component readyToRender]; + }]; +} + +- (void)updateAttributes:(NSDictionary *)attributes forComponent:(NSString *)ref +{ + WXAssertParam(attributes); + WXAssertParam(ref); + + WXComponent *component = [_indexDict objectForKey:ref]; + WXAssertComponentExist(component); + + [component _updateAttributesOnComponentThread:attributes]; + __weak typeof(self) weakSelf = self; + [self _addUITask:^{ + [WXTracingManager startTracingWithInstanceId:weakSelf.weexInstance.instanceId ref:ref className:nil name:nil phase:WXTracingBegin functionName:@"updateAttrs" options:@{@"threadName":WXTUIThread}]; + [component _updateAttributesOnMainThread:attributes]; + [component readyToRender]; + [WXTracingManager startTracingWithInstanceId:weakSelf.weexInstance.instanceId ref:ref className:nil name:nil phase:WXTracingEnd functionName:@"updateAttrs" options:@{@"threadName":WXTUIThread}]; + }]; +} + +- (void)addEvent:(NSString *)eventName toComponent:(NSString *)ref +{ + WXAssertComponentThread(); + WXAssertParam(eventName); + WXAssertParam(ref); + + WXComponent *component = [_indexDict objectForKey:ref]; + WXAssertComponentExist(component); + + [component _addEventOnComponentThread:eventName]; + + [self _addUITask:^{ + [component _addEventOnMainThread:eventName]; + }]; +} + +- (void)removeEvent:(NSString *)eventName fromComponent:(NSString *)ref +{ + WXAssertComponentThread(); + WXAssertParam(eventName); + WXAssertParam(ref); + + WXComponent *component = [_indexDict objectForKey:ref]; + WXAssertComponentExist(component); + + [component _removeEventOnComponentThread:eventName]; + + [self _addUITask:^{ + [component _removeEventOnMainThread:eventName]; + }]; +} + +- (void)scrollToComponent:(NSString *)ref options:(NSDictionary *)options +{ + WXAssertComponentThread(); + WXAssertParam(ref); + + WXComponent *toComponent = [_indexDict objectForKey:ref]; + WXAssertComponentExist(toComponent); + + id<WXScrollerProtocol> scrollerComponent = toComponent.ancestorScroller; + if (!scrollerComponent) { + return; + } + + CGFloat offset = [[options objectForKey:@"offset"] floatValue]; + BOOL animated = YES; + if ([options objectForKey:@"animated"]) { + animated = [[options objectForKey:@"animated"] boolValue]; + } + + [self _addUITask:^{ + [scrollerComponent scrollToComponent:toComponent withOffset:offset animated:animated]; + }]; +} + +#pragma mark Life Cycle + +- (void)createFinish +{ + WXAssertComponentThread(); + + WXSDKInstance *instance = self.weexInstance; + [self _addUITask:^{ + UIView *rootView = instance.rootView; + + //WX_MONITOR_INSTANCE_PERF_END(WXPTFirstScreenRender, instance); + WX_MONITOR_INSTANCE_PERF_END(WXPTAllRender, instance); + WX_MONITOR_SUCCESS(WXMTJSBridge); + WX_MONITOR_SUCCESS(WXMTNativeRender); + + if(instance.renderFinish){ + [WXTracingManager startTracingWithInstanceId:instance.instanceId ref:nil className:nil name:nil phase:WXTracingInstant functionName:WXTRenderFinish options:@{@"threadName":WXTUIThread}]; + instance.renderFinish(rootView); + } + }]; + [instance updatePerDicAfterCreateFinish]; +} + +- (void)updateFinish +{ + WXAssertComponentThread(); + + WXSDKInstance *instance = self.weexInstance; + WXComponent *root = [_indexDict objectForKey:WX_SDK_ROOT_REF]; + + [self _addUITask:^{ + if(instance.updateFinish){ + instance.updateFinish(root.view); + } + }]; +} + +- (void)refreshFinish +{ + WXAssertComponentThread(); + + WXSDKInstance *instance = self.weexInstance; + WXComponent *root = [_indexDict objectForKey:WX_SDK_ROOT_REF]; + + [self _addUITask:^{ + if(instance.refreshFinish){ + instance.refreshFinish(root.view); + } + }]; +} + +- (void)unload +{ + WXAssertComponentThread(); + [self invalidate]; + [self _stopDisplayLink]; + NSEnumerator *enumerator = [[_indexDict copy] objectEnumerator]; + dispatch_async(dispatch_get_main_queue(), ^{ + WXComponent *component; + while ((component = [enumerator nextObject])) { + [component _unloadViewWithReusing:NO]; + } + _rootComponent = nil; + }); + + [_indexDict removeAllObjects]; + [_uiTaskQueue removeAllObjects]; +} + +- (void)invalidate +{ + _isValid = NO; +} + +- (BOOL)isValid +{ + return _isValid; +} + +#pragma mark Layout Batch + +- (void)_startDisplayLink +{ + WXAssertComponentThread(); + + if(!_displayLink){ + _displayLink = [CADisplayLink displayLinkWithTarget:self selector:@selector(_handleDisplayLink)]; + [_displayLink addToRunLoop:[NSRunLoop currentRunLoop] forMode:NSDefaultRunLoopMode]; + } +} + +- (void)_stopDisplayLink +{ + WXAssertComponentThread(); + + if(_displayLink){ + [_displayLink invalidate]; + _displayLink = nil; + } +} + +- (void)_suspendDisplayLink +{ + WXAssertComponentThread(); + + if(_displayLink && !_displayLink.paused) { + _displayLink.paused = YES; + } +} + +- (void)_awakeDisplayLink +{ + WXAssertComponentThread(); + + if(_displayLink && _displayLink.paused) { + _displayLink.paused = NO; + } +} + +- (void)_handleDisplayLink +{ + WXAssertComponentThread(); + + [self _layoutAndSyncUI]; +} + +- (void)_layoutAndSyncUI +{ + [self _layout]; + if(_uiTaskQueue.count > 0){ + [self _syncUITasks]; + _noTaskTickCount = 0; + } else { + // suspend display link when there's no task for 1 second, in order to save CPU time. + _noTaskTickCount ++; + if (_noTaskTickCount > 60) { + [self _suspendDisplayLink]; + } + } +} + +- (void)_layout +{ + BOOL needsLayout = NO; + +// NSEnumerator *enumerator = [_indexDict objectEnumerator]; +// WXComponent *component; +// while ((component = [enumerator nextObject])) { +// if ([component needsLayout]) { +// needsLayout = YES; +// break; +// } +// } + + needsLayout = [_rootComponent needsLayout]; + + if (!needsLayout) { + return; + } +#ifdef DEBUG + WXLogDebug(@"flexLayout -> action__ calculateLayout root"); +#endif + +//#ifndef USE_FLEX + if(![WXComponent isUseFlex]) + { + layoutNode(_rootCSSNode, _rootCSSNode->style.dimensions[CSS_WIDTH], _rootCSSNode->style.dimensions[CSS_HEIGHT], CSS_DIRECTION_INHERIT); + } +//#else + else + { + std::pair<float, float> renderPageSize; + renderPageSize.first = self.weexInstance.frame.size.width; + renderPageSize.second = self.weexInstance.frame.size.height; + _rootFlexCSSNode->calculateLayout(renderPageSize); + } +//#endif + NSMutableSet<WXComponent *> *dirtyComponents = [NSMutableSet set]; + [_rootComponent _calculateFrameWithSuperAbsolutePosition:CGPointZero gatherDirtyComponents:dirtyComponents]; + [self _calculateRootFrame]; + + for (WXComponent *dirtyComponent in dirtyComponents) { + [self _addUITask:^{ + [dirtyComponent _layoutDidFinish]; + }]; + } +} + +//#ifdef USE_FLEX +- (void) _printFlexComonentFrame:(WXComponent *)component +{ +#ifdef DEBUG + WXLogDebug(@"node ref:%@, type:%@ , frame:%@", + component.ref, + component.type, + NSStringFromCGRect(component.view.layer.frame) + ); +#endif + + + + for (WXComponent *childComponent in component.subcomponents) { + [self _printFlexComonentFrame:childComponent]; + } + + +} +//#endif + +- (void)_syncUITasks +{ + NSArray<dispatch_block_t> *blocks = _uiTaskQueue; + _uiTaskQueue = [NSMutableArray array]; + dispatch_async(dispatch_get_main_queue(), ^{ + for(dispatch_block_t block in blocks) { + block(); + } + }); +} +//#ifndef USE_FLEX +- (void)_initRootCSSNode +{ + _rootCSSNode = new_css_node(); + + [self _applyRootFrame:self.weexInstance.frame toRootCSSNode:_rootCSSNode]; + + _rootCSSNode->style.flex_wrap = CSS_NOWRAP; + _rootCSSNode->is_dirty = rootNodeIsDirty; + _rootCSSNode->get_child = rootNodeGetChild; + _rootCSSNode->context=(__bridge void *)(self); + _rootCSSNode->children_count = 1; +} +//#else +- (void)_initRootFlexCssNode +{ + _rootFlexCSSNode = new WeexCore::WXCoreLayoutNode(); + [self _applyRootFrame:self.weexInstance.frame]; + _rootFlexCSSNode->setFlexWrap(WeexCore::kNoWrap); + _rootFlexCSSNode->setContext((__bridge void *)(self)); +} +//#endif + +- (void)_calculateRootFrame +{ +//#ifndef USE_FLEX + + if(![WXComponent isUseFlex]) + { + if (!_rootCSSNode->layout.should_update) { + return; + } + _rootCSSNode->layout.should_update = false; +#ifdef DEBUG + WXLogDebug(@"flexLayout -> root _calculateRootFrame"); +#endif + + CGRect frame = CGRectMake(WXRoundPixelValue(_rootCSSNode->layout.position[CSS_LEFT]), + WXRoundPixelValue(_rootCSSNode->layout.position[CSS_TOP]), + WXRoundPixelValue(_rootCSSNode->layout.dimensions[CSS_WIDTH]), + WXRoundPixelValue(_rootCSSNode->layout.dimensions[CSS_HEIGHT])); + WXPerformBlockOnMainThread(^{ + if(!self.weexInstance.isRootViewFrozen) { + self.weexInstance.rootView.frame = frame; + } + }); + + resetNodeLayout(_rootCSSNode); + } +//#else + else + { + if(!_rootFlexCSSNode->hasNewLayout()){ + return; + } + _rootFlexCSSNode->setHasNewLayout(false); +#ifdef DEBUG + WXLogDebug(@"flexLayout -> root _calculateRootFrame"); +#endif + + + CGRect frame = CGRectMake(WXRoundPixelValue(_rootFlexCSSNode->getLayoutPositionLeft()), + WXRoundPixelValue(_rootFlexCSSNode->getLayoutPositionTop()), + WXRoundPixelValue(_rootFlexCSSNode->getLayoutWidth()), + WXRoundPixelValue(_rootFlexCSSNode->getLayoutHeight())); + WXPerformBlockOnMainThread(^{ + if(!self.weexInstance.isRootViewFrozen) { + self.weexInstance.rootView.frame = frame; + } + }); + // _rootFlexCSSNode->reset(); + + // resetNodeLayout(_rootFlexCSSNode); + } +//#endif + + +} + + +#pragma mark Fixed + +- (void)addFixedComponent:(WXComponent *)fixComponent +{ + [_fixedComponents addObject:fixComponent]; +//#ifndef USE_FLEX + if(![WXComponent isUseFlex]) + { + _rootCSSNode->children_count = (int)[_fixedComponents count] + 1; + } +//#else + else + { + _rootFlexCSSNode->addChildAt(fixComponent.flexCssNode, (uint32_t)([_fixedComponents count]-1)); + } +//#endif +} + +- (void)removeFixedComponent:(WXComponent *)fixComponent +{ + [_fixedComponents removeObject:fixComponent]; +//#ifndef USE_FLEX + if(![WXComponent isUseFlex]) + { + _rootCSSNode->children_count = (int)[_fixedComponents count] + 1; + } +//#else + else + { + _rootFlexCSSNode->removeChild(fixComponent->_flexCssNode); + } +//#endif +} + +@end + +void WXPerformBlockOnComponentThread(void (^block)(void)) +{ + [WXComponentManager _performBlockOnComponentThread:block]; +} + +void WXPerformBlockSyncOnComponentThread(void (^block)(void)) +{ + [WXComponentManager _performBlockSyncOnComponentThread:block]; +}
http://git-wip-us.apache.org/repos/asf/incubator-weex/blob/b77b4259/ios/sdk/WeexSDK/Sources/Model/WXComponent.h ---------------------------------------------------------------------- diff --git a/ios/sdk/WeexSDK/Sources/Model/WXComponent.h b/ios/sdk/WeexSDK/Sources/Model/WXComponent.h index 5e8ba70..c0e6c9a 100644 --- a/ios/sdk/WeexSDK/Sources/Model/WXComponent.h +++ b/ios/sdk/WeexSDK/Sources/Model/WXComponent.h @@ -17,6 +17,12 @@ * under the License. */ +/** + * def : use weex_flex_engin + * ndef: use yoga + **/ + + #import <Foundation/Foundation.h> #import "WXLayoutDefine.h" #import "WXType.h" @@ -143,13 +149,6 @@ NS_ASSUME_NONNULL_BEGIN //@property(nonatomic, assign) CGPoint absolutePosition; /** - * @abstract Return the css node used to layout. - * - * @warning Subclasses must not override this. - */ -@property(nonatomic, readonly, assign) css_node_t *cssNode; - -/** * @abstract Invalidates the component's layout and marks it as needing an update. * * @discussion You can call this method to indicate that the layout of a component has changed and must be updated. Weex typically calls this method automatically when the layout-related styles change or when subcomponents are added or removed. http://git-wip-us.apache.org/repos/asf/incubator-weex/blob/b77b4259/ios/sdk/WeexSDK/Sources/Model/WXComponent.m ---------------------------------------------------------------------- diff --git a/ios/sdk/WeexSDK/Sources/Model/WXComponent.m b/ios/sdk/WeexSDK/Sources/Model/WXComponent.m deleted file mode 100644 index 300dbc9..0000000 --- a/ios/sdk/WeexSDK/Sources/Model/WXComponent.m +++ /dev/null @@ -1,842 +0,0 @@ -/* - * 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.h" -#import "WXComponent_internal.h" -#import "WXComponentManager.h" -#import "WXSDKManager.h" -#import "WXSDKInstance.h" -#import "WXSDKInstance_private.h" -#import "WXDefine.h" -#import "WXLog.h" -#import "WXWeakObjectWrapper.h" -#import "WXUtility.h" -#import "WXConvert.h" -#import "WXMonitor.h" -#import "WXAssert.h" -#import "WXThreadSafeMutableDictionary.h" -#import "WXThreadSafeMutableArray.h" -#import "WXTransform.h" -#import "WXRoundedRect.h" -#import <pthread/pthread.h> -#import "WXComponent+PseudoClassManagement.h" -#import "WXComponent+BoxShadow.h" -#import "WXTracingManager.h" -#import "WXComponent+Events.h" - -#pragma clang diagnostic ignored "-Wincomplete-implementation" -#pragma clang diagnostic ignored "-Wobjc-protocol-method-implementation" - -@interface WXComponent () <UIGestureRecognizerDelegate> - -@end - -@implementation WXComponent -{ -@private - NSString *_ref; - NSMutableDictionary *_styles; - NSMutableDictionary *_attributes; - NSMutableArray *_events; - - // Protects properties styles/attributes/events/subcomponents which will be accessed from multiple threads. - pthread_mutex_t _propertyMutex; - pthread_mutexattr_t _propertMutexAttr; - - __weak WXComponent *_supercomponent; - __weak id<WXScrollerProtocol> _ancestorScroller; - __weak WXSDKInstance *_weexInstance; -} - -#pragma mark Life Cycle - -- (instancetype)initWithRef:(NSString *)ref - type:(NSString *)type - styles:(NSDictionary *)styles - attributes:(NSDictionary *)attributes - events:(NSArray *)events - weexInstance:(WXSDKInstance *)weexInstance -{ - if (self = [super init]) { - pthread_mutexattr_init(&_propertMutexAttr); - pthread_mutexattr_settype(&_propertMutexAttr, PTHREAD_MUTEX_RECURSIVE); - pthread_mutex_init(&_propertyMutex, &_propertMutexAttr); - - _ref = ref; - _type = type; - _weexInstance = weexInstance; - _componentType = WXComponentTypeCommon; - _styles = [self parseStyles:styles]; - _attributes = attributes ? [NSMutableDictionary dictionaryWithDictionary:attributes] : [NSMutableDictionary dictionary]; - _events = events ? [NSMutableArray arrayWithArray:events] : [NSMutableArray array]; - _subcomponents = [NSMutableArray array]; - _absolutePosition = CGPointMake(NAN, NAN); - - _displayType = WXDisplayTypeBlock; - _isNeedJoinLayoutSystem = YES; - _isLayoutDirty = YES; - _isViewFrameSyncWithCalculated = YES; - _ariaHidden = nil; - _accessible = nil; - _accessibilityHintContent = nil; - - _async = NO; - - if (styles[kWXTransitionProperty]) { - _transition = [[WXTransition alloc]initWithStyles:styles]; - } - - //TODO set indicator style - if ([type isEqualToString:@"indicator"]) { - _styles[@"position"] = @"absolute"; - if (!_styles[@"left"] && !_styles[@"right"]) { - _styles[@"left"] = @0.0f; - } - if (!_styles[@"top"] && !_styles[@"bottom"]) { - _styles[@"top"] = @0.0f; - } - } - - if (attributes[@"ariaHidden"]) { - - _ariaHidden = [WXConvert NSString:attributes[@"ariaHidden"]]; - } - if (attributes[@"role"]) { - _roles = attributes[@"role"]; - } - if (attributes[@"ariaLabel"]) { - _ariaLabel = [WXConvert NSString:attributes[@"ariaLabel"]]; - } - if (attributes[@"accessible"]) { - _accessible = [WXConvert NSString:attributes[@"accessible"]]; - } - if(attributes[@"accessibilityHint"]) { - _accessibilityHintContent = [WXConvert NSString:attributes[@"accessibilityHint"]]; - } - if (attributes[@"groupAccessibilityChildren"]) { - _groupAccessibilityChildren = [WXConvert NSString:attributes[@"groupAccessibilityChildren"]]; - } - - if (attributes[@"testId"]) { - _testId = [WXConvert NSString:attributes[@"testId"]]; - } - - [self _setupNavBarWithStyles:_styles attributes:_attributes]; - [self _initCSSNodeWithStyles:_styles]; - [self _initViewPropertyWithStyles:_styles]; - [self _initCompositingAttribute:_attributes]; - [self _handleBorders:styles isUpdating:NO]; - - } - - return self; -} - -- (id)copyWithZone:(NSZone *)zone -{ - NSInteger copyId = 0; - @synchronized(self){ - static NSInteger __copy = 0; - copyId = __copy % (1024*1024); - __copy++; - } - NSString *copyRef = [NSString stringWithFormat:@"%ldcopy_of%@", (long)copyId, _isTemplate ? self.ref : self->_templateComponent.ref]; - WXComponent *component = [[[self class] allocWithZone:zone] initWithRef:copyRef type:self.type styles:self.styles attributes:self.attributes events:self.events weexInstance:self.weexInstance]; - if (_isTemplate) { - component->_templateComponent = self; - } else { - component->_templateComponent = self->_templateComponent; - } - memcpy(component->_cssNode, self.cssNode, sizeof(css_node_t)); - component->_cssNode->context = (__bridge void *)component; - component->_calculatedFrame = self.calculatedFrame; - - NSMutableArray *subcomponentsCopy = [NSMutableArray array]; - for (WXComponent *subcomponent in self.subcomponents) { - WXComponent *subcomponentCopy = [subcomponent copy]; - subcomponentCopy->_supercomponent = component; - [subcomponentsCopy addObject:subcomponentCopy]; - } - - component->_subcomponents = subcomponentsCopy; - - WXPerformBlockOnComponentThread(^{ - [self.weexInstance.componentManager addComponent:component toIndexDictForRef:copyRef]; - }); - - return component; -} - -- (UIAccessibilityTraits)_parseAccessibilityTraitsWithTraits:(UIAccessibilityTraits)trait roles:(NSString*)roleStr -{ - UIAccessibilityTraits newTrait = trait; - for (NSString * role in [roleStr componentsSeparatedByString:@" "]) { - newTrait |= [WXConvert WXUIAccessibilityTraits: role]; - } - - return newTrait; -} - -- (void)dealloc -{ - free_css_node(_cssNode); - -// [self _removeAllEvents]; - // remove all gesture and all - if (_isTemplate && self.attributes[@"@templateId"]) { - [[WXSDKManager bridgeMgr] callComponentHook:_weexInstance.instanceId componentId:self.attributes[@"@templateId"] type:@"lifecycle" hook:@"destroy" args:nil competion:nil]; - } - if (_tapGesture) { - [_tapGesture removeTarget:nil action:NULL]; - } - if ([_swipeGestures count]) { - for (UISwipeGestureRecognizer *swipeGestures in _swipeGestures) { - [swipeGestures removeTarget:nil action:NULL]; - } - } - - if (_longPressGesture) { - [_longPressGesture removeTarget:nil action:NULL]; - } - - if (_panGesture) { - [_panGesture removeTarget:nil action:NULL]; - } - - if (_positionType == WXPositionTypeFixed) { - [self.weexInstance.componentManager removeFixedComponent:self]; - } - - pthread_mutex_destroy(&_propertyMutex); - pthread_mutexattr_destroy(&_propertMutexAttr); - -} - -- (NSDictionary *)styles -{ - NSDictionary *styles; - pthread_mutex_lock(&_propertyMutex); - styles = _styles; - pthread_mutex_unlock(&_propertyMutex); - return styles; -} - -- (NSDictionary *)pseudoClassStyles -{ - NSDictionary *pseudoClassStyles; - pthread_mutex_lock(&_propertyMutex); - pseudoClassStyles = _pseudoClassStyles; - pthread_mutex_unlock(&_propertyMutex); - - return pseudoClassStyles; -} - -- (NSString *)type -{ - return _type; -} - -- (NSDictionary *)attributes -{ - NSDictionary *attributes; - pthread_mutex_lock(&_propertyMutex); - attributes = _attributes; - pthread_mutex_unlock(&_propertyMutex); - - return attributes; -} - -- (NSArray *)events -{ - NSArray *events; - pthread_mutex_lock(&_propertyMutex); - events = [_events copy]; - pthread_mutex_unlock(&_propertyMutex); - - return events; -} - -- (void)setDisplayType:(WXDisplayType)displayType -{ - if (_displayType != displayType) { - _displayType = displayType; - if (displayType == WXDisplayTypeNone) { - _isNeedJoinLayoutSystem = NO; - [self.supercomponent _recomputeCSSNodeChildren]; - WXPerformBlockOnMainThread(^{ - [self removeFromSuperview]; - }); - } else { - _isNeedJoinLayoutSystem = YES; - [self.supercomponent _recomputeCSSNodeChildren]; - WXPerformBlockOnMainThread(^{ - [self _buildViewHierarchyLazily]; - // TODO: insert into the correct index - [self.supercomponent.view addSubview:self.view]; - }); - } - [self setNeedsLayout]; - } -} - -- (WXSDKInstance *)weexInstance -{ - return _weexInstance; -} - -- (NSString *)description -{ - return [NSString stringWithFormat:@"<%@:%p ref=%@> %@", _type, self, _ref, _view]; -} - -#pragma mark Property - -- (UIView *)view -{ - if (_componentType != WXComponentTypeCommon) { - return nil; - } - if ([self isViewLoaded]) { - return _view; - } else { - WXAssertMainThread(); - - // compositing child will be drew by its composited ancestor - if (_isCompositingChild) { - return nil; - } - - [self viewWillLoad]; - - _view = [self loadView]; - - _layer = _view.layer; - _view.frame = _calculatedFrame; - _view.hidden = _visibility == WXVisibilityShow ? NO : YES; - _view.clipsToBounds = _clipToBounds; - if (![self _needsDrawBorder]) { - _layer.borderColor = _borderTopColor.CGColor; - _layer.borderWidth = _borderTopWidth; - [self _resetNativeBorderRadius]; - _layer.opacity = _opacity; - _view.backgroundColor = _backgroundColor; - } - - if (_backgroundImage) { - [self setGradientLayer]; - } - - if (_transform) { - [_transform applyTransformForView:_view]; - } - - if (_boxShadow) { - [self configBoxShadow:_boxShadow]; - } - - _view.wx_component = self; - _view.wx_ref = self.ref; - _layer.wx_component = self; - - if (_roles) { - [_view setAccessibilityTraits:[self _parseAccessibilityTraitsWithTraits:self.view.accessibilityTraits roles:_roles]]; - } - - if (_testId) { - _view.accessibilityIdentifier = _testId; - } - - if (_accessibilityHintContent) { - [_view setAccessibilityHint:_accessibilityHintContent]; - } - - if (_ariaLabel) { - _view.accessibilityLabel = _ariaLabel; - } - if (_accessible) { - [_view setIsAccessibilityElement:[WXConvert BOOL:_accessible]]; - } - - if (_ariaHidden) { - [_view setAccessibilityElementsHidden:[WXConvert BOOL:_ariaHidden]]; - } - if (_groupAccessibilityChildren) { - [_view setShouldGroupAccessibilityChildren:[WXConvert BOOL:_groupAccessibilityChildren]]; - } - - [self _initEvents:self.events]; - [self _initPseudoEvents:_isListenPseudoTouch]; - - if (_positionType == WXPositionTypeSticky) { - [self.ancestorScroller addStickyComponent:self]; - } - - if (self.supercomponent && self.supercomponent->_async) { - self->_async = YES; - } - - [self setNeedsDisplay]; - [[NSNotificationCenter defaultCenter] postNotificationName:WX_COMPONENT_NOTIFICATION_VIEW_LOADED object:self]; - [self viewDidLoad]; - - if (_lazyCreateView) { - [self _buildViewHierarchyLazily]; - } - - [self _handleFirstScreenTime]; - - return _view; - } -} - -- (void)_buildViewHierarchyLazily -{ - if (self.supercomponent && !((WXComponent *)self.supercomponent)->_lazyCreateView) { - NSArray *subcomponents = ((WXComponent *)self.supercomponent).subcomponents; - - NSInteger index = [subcomponents indexOfObject:self]; - if (index != NSNotFound) { - [(WXComponent *)self.supercomponent insertSubview:self atIndex:index]; - } - } - - NSArray *subcomponents = self.subcomponents; - for (int i = 0; i < subcomponents.count; i++) { - WXComponent *subcomponent = subcomponents[i]; - [self insertSubview:subcomponent atIndex:i]; - } -} - -- (void)_resetNativeBorderRadius -{ - WXRoundedRect *borderRect = [[WXRoundedRect alloc] initWithRect:_calculatedFrame topLeft:_borderTopLeftRadius topRight:_borderTopRightRadius bottomLeft:_borderBottomLeftRadius bottomRight:_borderBottomRightRadius]; - _layer.cornerRadius = borderRect.radii.topLeft; -} - -- (void)_handleFirstScreenTime -{ - if (WX_MONITOR_INSTANCE_PERF_IS_RECORDED(WXPTFirstScreenRender, self.weexInstance)) { - return; - } - CGPoint absolutePosition = [self.supercomponent.view convertPoint:_view.frame.origin toView:_weexInstance.rootView]; - if (absolutePosition.y + _view.frame.size.height > self.weexInstance.rootView.frame.size.height + 1) { - WX_MONITOR_INSTANCE_PERF_END(WXPTFirstScreenRender, self.weexInstance); - } -} - -- (CALayer *)layer -{ - return _layer; -} - -- (CGRect)calculatedFrame -{ - return _calculatedFrame; -} - -- (CGPoint)absolutePosition -{ - return _absolutePosition; -} - -- (css_node_t *)cssNode -{ - return _cssNode; -} - -- (void)_addEventParams:(NSDictionary *)params -{ - pthread_mutex_lock(&_propertyMutex); - if (!_eventParameters) { - _eventParameters = [NSMutableDictionary dictionary]; - } - [_eventParameters addEntriesFromDictionary:params]; - pthread_mutex_unlock(&_propertyMutex); -} - -- (NSArray *)_paramsForEvent:(NSString *)eventName -{ - NSArray *params; - pthread_mutex_lock(&_propertyMutex); - params = _eventParameters[eventName]; - pthread_mutex_unlock(&_propertyMutex); - - return params; -} - -#pragma mark Component Hierarchy - -- (NSArray<WXComponent *> *)subcomponents -{ - NSArray<WXComponent *> *subcomponents; - pthread_mutex_lock(&_propertyMutex); - subcomponents = [_subcomponents copy]; - pthread_mutex_unlock(&_propertyMutex); - - return subcomponents; -} - -- (WXComponent *)supercomponent -{ - return _supercomponent; -} - -- (void)_insertSubcomponent:(WXComponent *)subcomponent atIndex:(NSInteger)index -{ - WXAssert(subcomponent, @"The subcomponent to insert to %@ at index %d must not be nil", self, index); - if (index > [_subcomponents count]) { - WXLogError(@"the index of inserted %ld is out of range as the current is %lu", (long)index, (unsigned long)[_subcomponents count]); - return; - } - - subcomponent->_supercomponent = self; - - pthread_mutex_lock(&_propertyMutex); - [_subcomponents insertObject:subcomponent atIndex:index]; - pthread_mutex_unlock(&_propertyMutex); - - if (subcomponent->_positionType == WXPositionTypeFixed) { - [self.weexInstance.componentManager addFixedComponent:subcomponent]; - subcomponent->_isNeedJoinLayoutSystem = NO; - } - - if (_useCompositing || _isCompositingChild) { - subcomponent->_isCompositingChild = YES; - } - - [self _recomputeCSSNodeChildren]; - [self setNeedsLayout]; -} - -- (void)_removeSubcomponent:(WXComponent *)subcomponent -{ - pthread_mutex_lock(&_propertyMutex); - [_subcomponents removeObject:subcomponent]; - pthread_mutex_unlock(&_propertyMutex); -} - -- (void)_removeFromSupercomponent -{ - [self.supercomponent _removeSubcomponent:self]; - [self.supercomponent _recomputeCSSNodeChildren]; - [self.supercomponent setNeedsLayout]; - - if (_positionType == WXPositionTypeFixed) { - [self.weexInstance.componentManager removeFixedComponent:self]; - self->_isNeedJoinLayoutSystem = YES; - } -} - -- (void)_moveToSupercomponent:(WXComponent *)newSupercomponent atIndex:(NSUInteger)index -{ - [self _removeFromSupercomponent]; - [newSupercomponent _insertSubcomponent:self atIndex:index]; -} - -- (void)_didInserted -{ - -} - -- (id<WXScrollerProtocol>)ancestorScroller -{ - if(!_ancestorScroller) { - WXComponent *supercomponent = self.supercomponent; - while (supercomponent) { - if([supercomponent conformsToProtocol:@protocol(WXScrollerProtocol)]) { - _ancestorScroller = (id<WXScrollerProtocol>)supercomponent; - break; - } - supercomponent = supercomponent.supercomponent; - } - } - - return _ancestorScroller; -} - -#pragma mark Updating -- (void)_updateStylesOnComponentThread:(NSDictionary *)styles resetStyles:(NSMutableArray *)resetStyles isUpdateStyles:(BOOL)isUpdateStyles -{ - BOOL isTransitionTag = _transition ? [self _isTransitionTag:styles] : NO; - if (isTransitionTag) { - [_transition _handleTransitionWithStyles:styles resetStyles:resetStyles target:self]; - } else { - styles = [self parseStyles:styles]; - [self _updateCSSNodeStyles:styles]; - [self _resetCSSNodeStyles:resetStyles]; - } - if (isUpdateStyles) { - [self _modifyStyles:styles]; - if ([self needsLayout]) { - // call update style may take effect on layout, maybe the component - // displaylink has been paused, so we need to restart the component task, and it will auto-pause when task queue is empty. - [self.weexInstance.componentManager startComponentTasks]; - } - } -} - -- (BOOL)_isTransitionTag:(NSDictionary *)styles -{ - BOOL yesOrNo = false; - if (_transition.transitionOptions != WXTransitionOptionsNone) { - yesOrNo = true; - } - return yesOrNo; -} - -- (BOOL)_isTransitionOnMainThreadStyles:(NSDictionary *)styles -{ - BOOL yesOrNo = false; - if (_transition.transitionOptions != WXTransitionOptionsNone) { - if ((_transition.transitionOptions & WXTransitionOptionsBackgroundColor &&styles[@"backgroundColor"]) - ||(_transition.transitionOptions & WXTransitionOptionsTransform &&styles[@"transform"]) - ||(_transition.transitionOptions & WXTransitionOptionsOpacity &&styles[@"opacity"])) { - yesOrNo = true; - } - } - return yesOrNo; -} - -- (void)_modifyStyles:(NSDictionary *)styles -{ - pthread_mutex_lock(&_propertyMutex); - [_styles addEntriesFromDictionary:styles]; - pthread_mutex_unlock(&_propertyMutex); -} - -- (void)_updateAttributesOnComponentThread:(NSDictionary *)attributes -{ - pthread_mutex_lock(&_propertyMutex); - [_attributes addEntriesFromDictionary:attributes]; - pthread_mutex_unlock(&_propertyMutex); -} - -- (void)_addEventOnComponentThread:(NSString *)eventName -{ - pthread_mutex_lock(&_propertyMutex); - [_events addObject:eventName]; - pthread_mutex_unlock(&_propertyMutex); -} - -- (void)_removeEventOnComponentThread:(NSString *)eventName -{ - pthread_mutex_lock(&_propertyMutex); - [_events removeObject:eventName]; - pthread_mutex_unlock(&_propertyMutex); -} - -- (void)_updateStylesOnMainThread:(NSDictionary *)styles resetStyles:(NSMutableArray *)resetStyles -{ - WXAssertMainThread(); - if (![self _isTransitionOnMainThreadStyles:styles]) { - [self _updateViewStyles:styles]; - } else { - [self _transitionUpdateViewProperty:styles]; - } - [self _resetStyles:resetStyles]; - [self _handleBorders:styles isUpdating:YES]; - [self updateStyles:styles]; - [self resetStyles:resetStyles]; -} - -- (void)_updateAttributesOnMainThread:(NSDictionary *)attributes -{ - WXAssertMainThread(); - - [self _updateNavBarAttributes:attributes]; - - [self updateAttributes:attributes]; - [self _configWXComponentA11yWithAttributes:attributes]; -} - -- (void)updateStyles:(NSDictionary *)styles -{ - WXAssertMainThread(); -} - -- (void)updateAttributes:(NSDictionary *)attributes -{ - WXAssertMainThread(); -} - -- (void)setNativeTransform:(CGAffineTransform)transform -{ - WXAssertMainThread(); - - _transform = [[WXTransform alloc] initWithNativeTransform:CATransform3DMakeAffineTransform(transform) instance:self.weexInstance]; - if (!CGRectEqualToRect(self.calculatedFrame, CGRectZero)) { - [_transform applyTransformForView:_view]; - [_layer setNeedsDisplay]; - } -} - -- (void)readyToRender -{ - if (self.weexInstance.trackComponent) { - [self.supercomponent readyToRender]; - } -} - - -- (void)setGradientLayer -{ - if (CGRectEqualToRect(self.view.frame, CGRectZero)) { - return; - } - NSDictionary * linearGradient = [WXUtility linearGradientWithBackgroundImage:_backgroundImage]; - if (!linearGradient) { - return ; - } - - __weak typeof(self) weakSelf = self; - dispatch_async(dispatch_get_main_queue(), ^{ - __strong typeof(self) strongSelf = weakSelf; - if(strongSelf) { - UIColor * startColor = (UIColor*)linearGradient[@"startColor"]; - UIColor * endColor = (UIColor*)linearGradient[@"endColor"]; - CAGradientLayer * gradientLayer = [WXUtility gradientLayerFromColors:@[startColor, endColor] locations:nil frame:strongSelf.view.bounds gradientType:[linearGradient[@"gradientType"] integerValue]]; - if (gradientLayer) { - _backgroundColor = [UIColor colorWithPatternImage:[strongSelf imageFromLayer:gradientLayer]]; - strongSelf.view.backgroundColor = _backgroundColor; - } - } - }); -} - -- (void)_configWXComponentA11yWithAttributes:(NSDictionary *)attributes -{ - WX_CHECK_COMPONENT_TYPE(self.componentType) - if (attributes[@"role"]){ - _roles = attributes[@"role"]; - [self.view setAccessibilityTraits:[self _parseAccessibilityTraitsWithTraits:self.view.accessibilityTraits roles:_roles]]; - } - if (attributes[@"ariaHidden"]) { - _ariaHidden = [WXConvert NSString:attributes[@"ariaHidden"]]; - [self.view setAccessibilityElementsHidden:[WXConvert BOOL:_ariaHidden]]; - } - if (attributes[@"accessible"]) { - _accessible = [WXConvert NSString:attributes[@"accessible"]]; - [self.view setIsAccessibilityElement:[WXConvert BOOL:_accessible]]; - } - if (attributes[@"ariaLabel"]) { - _ariaLabel = [WXConvert NSString:attributes[@"ariaLabel"]]; - self.view.accessibilityValue = _ariaLabel; - } - if (attributes[@"accessibilityHint"]) { - _accessibilityHintContent = [WXConvert NSString:attributes[@"accessibilityHint"]]; - [self.view setAccessibilityHint:_accessibilityHintContent]; - } - - if (attributes[@"groupAccessibilityChildren"]) { - _groupAccessibilityChildren = [WXConvert NSString:attributes[@"groupAccessibilityChildren"]]; - [self.view setShouldGroupAccessibilityChildren:[WXConvert BOOL:_groupAccessibilityChildren]]; - } - - - if (attributes[@"testId"]) { - [self.view setAccessibilityIdentifier:[WXConvert NSString:attributes[@"testId"]]]; - } - -} - -- (UIImage *)imageFromLayer:(CALayer *)layer -{ - UIGraphicsBeginImageContextWithOptions(layer.frame.size, NO, 0); - [layer renderInContext:UIGraphicsGetCurrentContext()]; - UIImage *outputImage = UIGraphicsGetImageFromCurrentImageContext(); - UIGraphicsEndImageContext(); - return outputImage; -} - -#pragma mark Reset -- (void)resetStyles:(NSArray *)styles -{ - WXAssertMainThread(); -} - -#pragma mark Layout - -/** - * @see WXComponent+Layout.m - */ - -#pragma mark View Management - -/** - * @see WXComponent+ViewManagement.m - */ - -#pragma mark Events - -/** - * @see WXComponent+Events.m - */ - -#pragma mark Display - -/** - * @see WXComponent+Display.m - */ - -@end - - -@implementation UIView (WXComponent) - -- (WXComponent *)wx_component -{ - WXWeakObjectWrapper *weakWrapper = objc_getAssociatedObject(self, @selector(wx_component)); - return [weakWrapper weakObject]; -} - -- (void)setWx_component:(WXComponent *)wx_component -{ - id weakWrapper = [[WXWeakObjectWrapper alloc] initWithWeakObject:wx_component]; - objc_setAssociatedObject(self, @selector(wx_component), weakWrapper, OBJC_ASSOCIATION_RETAIN_NONATOMIC); -} - -- (NSString *)wx_ref -{ - WXWeakObjectWrapper *weakWrapper = objc_getAssociatedObject(self, @selector(wx_ref)); - return [weakWrapper weakObject]; -} - -- (void)setWx_ref:(NSString *)wx_ref -{ - id weakWrapper = [[WXWeakObjectWrapper alloc] initWithWeakObject:wx_ref]; - objc_setAssociatedObject(self, @selector(wx_ref), weakWrapper, OBJC_ASSOCIATION_RETAIN_NONATOMIC); -} - -@end - -@implementation CALayer (WXComponents_new) - -- (WXComponent *)wx_component -{ - WXWeakObjectWrapper *weakWrapper = objc_getAssociatedObject(self, @selector(wx_component)); - return [weakWrapper weakObject]; -} - -- (void)setWx_component:(WXComponent *)wx_component -{ - id weakWrapper = [[WXWeakObjectWrapper alloc] initWithWeakObject:wx_component]; - objc_setAssociatedObject(self, @selector(wx_component), weakWrapper, OBJC_ASSOCIATION_RETAIN_NONATOMIC); -} - -@end