Merge pull request #3350 from jianhan-he/master
For release version
diff --git a/WeexSDK.podspec b/WeexSDK.podspec
index 8689ebb..869e849 100644
--- a/WeexSDK.podspec
+++ b/WeexSDK.podspec
@@ -69,6 +69,7 @@
'ios/sdk/WeexSDK/Sources/Controller/WXBaseViewController.h',
'ios/sdk/WeexSDK/Sources/Controller/WXRootViewController.h',
'ios/sdk/WeexSDK/Sources/Handler/WXNavigationDefaultImpl.h',
+ 'ios/sdk/WeexSDK/Sources/Handler/WXUnicornEventListenerHandler.h',
'ios/sdk/WeexSDK/Sources/View/WXView.h',
'ios/sdk/WeexSDK/Sources/View/WXErrorView.h',
'ios/sdk/WeexSDK/Sources/Protocol/*.h',
diff --git a/ios/sdk/WeexSDK.xcodeproj/project.pbxproj b/ios/sdk/WeexSDK.xcodeproj/project.pbxproj
index d14d0a7..d6b88ee 100644
--- a/ios/sdk/WeexSDK.xcodeproj/project.pbxproj
+++ b/ios/sdk/WeexSDK.xcodeproj/project.pbxproj
@@ -583,6 +583,11 @@
BDB1129F2459D8FC008492F9 /* reactor_page.cpp in Sources */ = {isa = PBXBuildFile; fileRef = BDB112992459D71E008492F9 /* reactor_page.cpp */; };
BDB112A02459D90F008492F9 /* reactor_page.h in Headers */ = {isa = PBXBuildFile; fileRef = BDB1129A2459D71E008492F9 /* reactor_page.h */; settings = {ATTRIBUTES = (Public, ); }; };
BDBA319B248D0A5200C6EDD0 /* reactor_page.cpp in Headers */ = {isa = PBXBuildFile; fileRef = BDB112992459D71E008492F9 /* reactor_page.cpp */; };
+ BDC402D925889EF900EA838A /* WXUnicornRenderProtocol.h in Headers */ = {isa = PBXBuildFile; fileRef = BDD94B6425761DAA002AD864 /* WXUnicornRenderProtocol.h */; settings = {ATTRIBUTES = (Public, ); }; };
+ BDC402DE25889F0900EA838A /* WXUnicornEventListenerHandler.h in Headers */ = {isa = PBXBuildFile; fileRef = BDD94B5125761CC7002AD864 /* WXUnicornEventListenerHandler.h */; settings = {ATTRIBUTES = (Public, ); }; };
+ BDD94B5225761CC7002AD864 /* WXUnicornEventListenerHandler.m in Sources */ = {isa = PBXBuildFile; fileRef = BDD94B5025761CC7002AD864 /* WXUnicornEventListenerHandler.m */; };
+ BDD94B5325761CC7002AD864 /* WXUnicornEventListenerHandler.h in Headers */ = {isa = PBXBuildFile; fileRef = BDD94B5125761CC7002AD864 /* WXUnicornEventListenerHandler.h */; settings = {ATTRIBUTES = (Public, ); }; };
+ BDD94B6525761DAA002AD864 /* WXUnicornRenderProtocol.h in Headers */ = {isa = PBXBuildFile; fileRef = BDD94B6425761DAA002AD864 /* WXUnicornRenderProtocol.h */; settings = {ATTRIBUTES = (Public, ); }; };
BDEEADBA22F2902E0099F1D7 /* time_calculator.cpp in Sources */ = {isa = PBXBuildFile; fileRef = BDEEADB822F2902D0099F1D7 /* time_calculator.cpp */; };
BDEEADBB22F2902E0099F1D7 /* time_calculator.cpp in Sources */ = {isa = PBXBuildFile; fileRef = BDEEADB822F2902D0099F1D7 /* time_calculator.cpp */; };
BDEEADBC22F2902E0099F1D7 /* time_calculator.h in Headers */ = {isa = PBXBuildFile; fileRef = BDEEADB922F2902E0099F1D7 /* time_calculator.h */; };
@@ -913,6 +918,8 @@
ED053503207F4DEB007B4568 /* JSContext+Weex.m in Sources */ = {isa = PBXBuildFile; fileRef = ED0534FF207F4DEB007B4568 /* JSContext+Weex.m */; };
F75C58EB2313C03C002FFF94 /* WXTimerModule.h in Headers */ = {isa = PBXBuildFile; fileRef = D3FC0DF51C508B2A002B9E31 /* WXTimerModule.h */; };
F75C591C2313C1FC002FFF94 /* WXStreamModule.h in Headers */ = {isa = PBXBuildFile; fileRef = 74A4BAA41CB4F98300195969 /* WXStreamModule.h */; settings = {ATTRIBUTES = (Public, ); }; };
+ F769EECA2578E3000027B29B /* WXLegacyAdapter.h in Headers */ = {isa = PBXBuildFile; fileRef = F769EEC92578E3000027B29B /* WXLegacyAdapter.h */; };
+ F769EECB2578E3000027B29B /* WXLegacyAdapter.h in Headers */ = {isa = PBXBuildFile; fileRef = F769EEC92578E3000027B29B /* WXLegacyAdapter.h */; };
/* End PBXBuildFile section */
/* Begin PBXContainerItemProxy section */
@@ -1342,6 +1349,9 @@
BDB112972459D6C2008492F9 /* WXReactorProtocol.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = WXReactorProtocol.h; sourceTree = "<group>"; };
BDB112992459D71E008492F9 /* reactor_page.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = reactor_page.cpp; sourceTree = "<group>"; };
BDB1129A2459D71E008492F9 /* reactor_page.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = reactor_page.h; sourceTree = "<group>"; };
+ BDD94B5025761CC7002AD864 /* WXUnicornEventListenerHandler.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = WXUnicornEventListenerHandler.m; sourceTree = "<group>"; };
+ BDD94B5125761CC7002AD864 /* WXUnicornEventListenerHandler.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = WXUnicornEventListenerHandler.h; sourceTree = "<group>"; };
+ BDD94B6425761DAA002AD864 /* WXUnicornRenderProtocol.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = WXUnicornRenderProtocol.h; sourceTree = "<group>"; };
BDEEADB822F2902D0099F1D7 /* time_calculator.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = time_calculator.cpp; sourceTree = "<group>"; };
BDEEADB922F2902E0099F1D7 /* time_calculator.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = time_calculator.h; sourceTree = "<group>"; };
C401945D1E344E8300D19C31 /* WXFloatCompareTests.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = WXFloatCompareTests.m; sourceTree = "<group>"; };
@@ -1420,6 +1430,7 @@
DCF343661E49CAEE00A2FB34 /* WXJSExceptionInfo.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = WXJSExceptionInfo.m; sourceTree = "<group>"; };
ED0534FE207F4DEB007B4568 /* JSContext+Weex.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = "JSContext+Weex.h"; sourceTree = "<group>"; };
ED0534FF207F4DEB007B4568 /* JSContext+Weex.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = "JSContext+Weex.m"; sourceTree = "<group>"; };
+ F769EEC92578E3000027B29B /* WXLegacyAdapter.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = WXLegacyAdapter.h; sourceTree = "<group>"; };
/* End PBXFileReference section */
/* Begin PBXFrameworksBuildPhase section */
@@ -1515,6 +1526,8 @@
59A583031CF5B2FD0081FD3E /* Handler */ = {
isa = PBXGroup;
children = (
+ BDD94B5125761CC7002AD864 /* WXUnicornEventListenerHandler.h */,
+ BDD94B5025761CC7002AD864 /* WXUnicornEventListenerHandler.m */,
D7D6B6E5238E1B2A00BE56DD /* WXDarkSchemeDefaultImpl.h */,
D7D6B6E4238E1B2A00BE56DD /* WXDarkSchemeDefaultImpl.m */,
33CE190C2153443000CF9670 /* WXJSFrameworkLoadDefaultImpl.h */,
@@ -1898,6 +1911,7 @@
77D1611C1C02DD3C0010B15B /* Protocol */ = {
isa = PBXGroup;
children = (
+ BDD94B6425761DAA002AD864 /* WXUnicornRenderProtocol.h */,
BDB112972459D6C2008492F9 /* WXReactorProtocol.h */,
D7D6B6E1238E1B1D00BE56DD /* WXDarkSchemeProtocol.h */,
33CE19122153444900CF9670 /* WXJSFrameworkLoadProtocol.h */,
@@ -1935,6 +1949,7 @@
77D161481C02E3670010B15B /* Utility */ = {
isa = PBXGroup;
children = (
+ F769EEC92578E3000027B29B /* WXLegacyAdapter.h */,
453F374B219A76A500A03F1D /* WXConvertUtility.h */,
453F374C219A76A500A03F1D /* WXConvertUtility.mm */,
C4D872231E5DDF7500E39BC1 /* WXBoxShadow.h */,
@@ -2462,6 +2477,8 @@
isa = PBXHeadersBuildPhase;
buildActionMask = 2147483647;
files = (
+ BDD94B5325761CC7002AD864 /* WXUnicornEventListenerHandler.h in Headers */,
+ BDD94B6525761DAA002AD864 /* WXUnicornRenderProtocol.h in Headers */,
BDB112982459D6C2008492F9 /* WXReactorProtocol.h in Headers */,
BDB1129C2459D71E008492F9 /* reactor_page.h in Headers */,
77E659F11C0C3612008B8775 /* WXModuleFactory.h in Headers */,
@@ -2618,6 +2635,7 @@
B8394F3721468AF100CA1EFF /* render_action_trigger_vsync.h in Headers */,
B8D66C8921255730003960BD /* render_object_interface.h in Headers */,
74BA4AB31F70F4B600AC29BF /* WXRecycleListLayout.h in Headers */,
+ F769EECA2578E3000027B29B /* WXLegacyAdapter.h in Headers */,
B8D66C3521255730003960BD /* render_action_move_element.h in Headers */,
742AD7311DF98C45007DC46C /* WXResourceRequestHandlerDefaultImpl.h in Headers */,
C4F0127D1E1502A6003378D0 /* WXWebSocketHandler.h in Headers */,
@@ -2718,6 +2736,8 @@
files = (
BDB1129D2459D802008492F9 /* WXReactorProtocol.h in Headers */,
BDB112A02459D90F008492F9 /* reactor_page.h in Headers */,
+ BDC402DE25889F0900EA838A /* WXUnicornEventListenerHandler.h in Headers */,
+ BDC402D925889EF900EA838A /* WXUnicornRenderProtocol.h in Headers */,
DCA4461D1EFA5AAA00D0CFA8 /* WXHandlerFactory.h in Headers */,
DCA446101EFA5A8500D0CFA8 /* WXBridgeMethod.h in Headers */,
DCA4461A1EFA5AA000D0CFA8 /* WXInvocationConfig.h in Headers */,
@@ -2872,6 +2892,7 @@
B8F3323D2141A4C600701BA0 /* string_util.h in Headers */,
77788B782229252D000D5102 /* render_page_base.h in Headers */,
DCA446091EFA5A6D00D0CFA8 /* WXThreadSafeCounter.h in Headers */,
+ F769EECB2578E3000027B29B /* WXLegacyAdapter.h in Headers */,
B8D66C3421255730003960BD /* render_action_update_attr.h in Headers */,
DCA445F11EFA5A2000D0CFA8 /* WXCanvasComponent.h in Headers */,
B8D66CB021255730003960BD /* wson_parser.h in Headers */,
@@ -3009,7 +3030,6 @@
isa = PBXNativeTarget;
buildConfigurationList = 77D161111C02DBE70010B15B /* Build configuration list for PBXNativeTarget "WeexSDK" */;
buildPhases = (
- 59D3CA601D003832008835DC /* Generate WeexSDK.h */,
77D160F81C02DBE70010B15B /* Sources */,
77D160F91C02DBE70010B15B /* Frameworks */,
77D160FA1C02DBE70010B15B /* Headers */,
@@ -3028,7 +3048,6 @@
isa = PBXNativeTarget;
buildConfigurationList = DCA4452C1EFA555400D0CFA8 /* Build configuration list for PBXNativeTarget "WeexSDK-Dynamic" */;
buildPhases = (
- DCA445C81EFA584000D0CFA8 /* Generate WeexSDK.h */,
DCA445201EFA555400D0CFA8 /* Sources */,
DCA445211EFA555400D0CFA8 /* Frameworks */,
DCA445221EFA555400D0CFA8 /* Headers */,
@@ -3143,35 +3162,6 @@
shellPath = /bin/sh;
shellScript = "# Sets the target folders and the final framework product.\n# 如果工程名称和Framework的Target名称不一样的话,要自定义FMKNAME\nFMK_NAME=${PROJECT_NAME}\n# Install dir will be the final output to the framework.\n# The following line create it in the root folder of the current project.\nINSTALL_DIR=${SRCROOT}/Products/${FMK_NAME}.framework\n# Working dir will be deleted after the framework creation.\nWRK_DIR=build\nDEVICE_DIR=${WRK_DIR}/Release-iphoneos/${FMK_NAME}.framework\nSIMULATOR_DIR=${WRK_DIR}/Release-iphonesimulator/${FMK_NAME}.framework\n# -configuration ${CONFIGURATION}\n# Clean and Building both architectures.\necho xcodebuild -configuration \"Release\" -target \"${FMK_NAME}\" -sdk iphoneos \"CODE_SIGN_IDENTITY=${CODE_SIGN_IDENTITY}\" clean build\nxcodebuild -configuration \"Release\" -target \"${FMK_NAME}\" -sdk iphoneos \"CODE_SIGN_IDENTITY=${CODE_SIGN_IDENTITY}\" clean build\nif [ \"$?\" != \"0\" ]; then\nexit 1\nfi\necho xcodebuild -configuration \"Release\" -target \"${FMK_NAME}\" -sdk iphonesimulator \"CODE_SIGN_IDENTITY=${CODE_SIGN_IDENTITY}\" build\nxcodebuild -configuration \"Release\" -target \"${FMK_NAME}\" -sdk iphonesimulator \"CODE_SIGN_IDENTITY=${CODE_SIGN_IDENTITY}\" build\nif [ \"$?\" != \"0\" ]; then\nexit 1\nfi\n# Cleaning the oldest.\nif [ -d \"${INSTALL_DIR}\" ]\nthen\nrm -rf \"${INSTALL_DIR}\"\nfi\nmkdir -p \"${INSTALL_DIR}\"\ncp -R \"${SIMULATOR_DIR}/\" \"${INSTALL_DIR}/\"\n# 移除签名资源和 Info.plist\nrm \"${INSTALL_DIR}/Info.plist\"\nrm -rf \"${INSTALL_DIR}/_CodeSignature\"\n# Uses the Lipo Tool to merge both binary files (i386 + armv6/armv7) into one Universal final product.\nlipo -create \"${DEVICE_DIR}/${FMK_NAME}\" \"${SIMULATOR_DIR}/${FMK_NAME}\" -output \"${INSTALL_DIR}/${FMK_NAME}\"\nrm -r \"${WRK_DIR}\"\n";
};
- 59D3CA601D003832008835DC /* Generate WeexSDK.h */ = {
- isa = PBXShellScriptBuildPhase;
- buildActionMask = 2147483647;
- files = (
- );
- inputPaths = (
- );
- name = "Generate WeexSDK.h";
- outputPaths = (
- );
- runOnlyForDeploymentPostprocessing = 0;
- shellPath = /bin/sh;
- shellScript = ". \"${PROJECT_DIR}/buildScripts.sh\"\n\ngenerateSDKHeader 'WeexSDK'\ngenerateBuildTime \"${PROJECT_DIR}/WeexSDK/Sources/Utility/WXVersion.m\"\n";
- };
- DCA445C81EFA584000D0CFA8 /* Generate WeexSDK.h */ = {
- isa = PBXShellScriptBuildPhase;
- buildActionMask = 2147483647;
- files = (
- );
- inputPaths = (
- );
- name = "Generate WeexSDK.h";
- outputPaths = (
- "$(PROJECT_DIR)/myfile",
- );
- runOnlyForDeploymentPostprocessing = 0;
- shellPath = /bin/sh;
- shellScript = ". \"${PROJECT_DIR}/buildScripts.sh\"\n\ngenerateSDKHeader 'WeexSDK'\ngenerateBuildTime \"${PROJECT_DIR}/WeexSDK/Sources/Utility/WXVersion.m\"\n";
- };
/* End PBXShellScriptBuildPhase section */
/* Begin PBXSourcesBuildPhase section */
@@ -3311,6 +3301,7 @@
DCAB35FF1D658EB700C0EA70 /* WXRuleManager.m in Sources */,
77D161251C02DDD10010B15B /* WXSDKInstance.m in Sources */,
DC7764931F3C2CA300B5727E /* WXRecyclerDragController.m in Sources */,
+ BDD94B5225761CC7002AD864 /* WXUnicornEventListenerHandler.m in Sources */,
744D61151E4AF23E00B624B3 /* WXDiffUtil.m in Sources */,
B8D66C4321255730003960BD /* render_action_remove_element.cpp in Sources */,
B8D66BFB2125572F003960BD /* render_performance.cpp in Sources */,
@@ -3886,6 +3877,7 @@
DYLIB_COMPATIBILITY_VERSION = 1;
DYLIB_CURRENT_VERSION = 1;
DYLIB_INSTALL_NAME_BASE = "@rpath";
+ GCC_OPTIMIZATION_LEVEL = z;
GCC_PRECOMPILE_PREFIX_HEADER = YES;
GCC_PREFIX_HEADER = "WeexSDK/Sources/Supporting Files/WeexSDK-Prefix.pch";
GCC_PREPROCESSOR_DEFINITIONS = (
@@ -3993,6 +3985,7 @@
DYLIB_CURRENT_VERSION = 1;
DYLIB_INSTALL_NAME_BASE = "@rpath";
ENABLE_BITCODE = NO;
+ GCC_OPTIMIZATION_LEVEL = z;
GCC_PRECOMPILE_PREFIX_HEADER = YES;
GCC_PREFIX_HEADER = "WeexSDK/Sources/Supporting Files/WeexSDK-Prefix.pch";
GCC_PREPROCESSOR_DEFINITIONS = (
diff --git a/ios/sdk/WeexSDK/Sources/Bridge/WXBridgeContext.m b/ios/sdk/WeexSDK/Sources/Bridge/WXBridgeContext.m
index 1df0948..2ba1fad 100644
--- a/ios/sdk/WeexSDK/Sources/Bridge/WXBridgeContext.m
+++ b/ios/sdk/WeexSDK/Sources/Bridge/WXBridgeContext.m
@@ -545,6 +545,7 @@
};
__weak typeof(self) weakSelf = self;
[sdkInstance.apmInstance onStage:KEY_PAGE_STAGES_LOAD_BUNDLE_END];
+ [sdkInstance.apmInstance onStage:KEY_PAGE_STAGES_CREATE_INSTANCE_START];
[self callJSMethod:@"createInstanceContext" args:@[instanceIdString, immutableOptions, data?:@[]] onContext:nil completion:^(JSValue *instanceContextEnvironment) {
if (sdkInstance.pageName) {
[sdkInstance.instanceJavaScriptContext.javaScriptContext setName:sdkInstance.pageName];
@@ -596,6 +597,7 @@
}
sdkInstance.instanceJavaScriptContext.javaScriptContext[@"wxExtFuncInfo"] = nil;
[sdkInstance.apmInstance onStage:KEY_PAGE_STAGES_EXECUTE_BUNDLE_END];
+ [sdkInstance.apmInstance onStage:KEY_PAGE_STAGES_CREATE_INSTANCE_END];
WX_MONITOR_INSTANCE_PERF_END(WXPTJSCreateInstance, [WXSDKManager instanceForID:instanceIdString]);
}];
}
@@ -603,6 +605,7 @@
} else {
[sdkInstance.apmInstance setProperty:KEY_PAGE_PROPERTIES_BUNDLE_TYPE withValue:@"other"];
[sdkInstance.apmInstance onStage:KEY_PAGE_STAGES_LOAD_BUNDLE_END];
+ [sdkInstance.apmInstance onStage:KEY_PAGE_STAGES_CREATE_INSTANCE_START];
if (data){
args = @[instanceIdString, jsBundleString, options ?: @{}, data];
} else {
@@ -617,6 +620,7 @@
[self callJSMethod:@"createInstance" args:args];
sdkInstance.instanceJavaScriptContext.javaScriptContext[@"wxExtFuncInfo"] = nil;
[sdkInstance.apmInstance onStage:KEY_PAGE_STAGES_EXECUTE_BUNDLE_END];
+ [sdkInstance.apmInstance onStage:KEY_PAGE_STAGES_CREATE_INSTANCE_END];
WX_MONITOR_INSTANCE_PERF_END(WXPTJSCreateInstance, [WXSDKManager instanceForID:instanceIdString]);
}
}
diff --git a/ios/sdk/WeexSDK/Sources/Bridge/WXCoreBridge.h b/ios/sdk/WeexSDK/Sources/Bridge/WXCoreBridge.h
index dd81f7f..4c120eb 100644
--- a/ios/sdk/WeexSDK/Sources/Bridge/WXCoreBridge.h
+++ b/ios/sdk/WeexSDK/Sources/Bridge/WXCoreBridge.h
@@ -20,6 +20,8 @@
#ifndef WXCORE_BRIDGE_PLATFORM_H
#define WXCORE_BRIDGE_PLATFORM_H
+#import <JavaScriptCore/JavaScriptCore.h>
+
#if defined __cplusplus
#include "core/bridge/platform_bridge.h"
@@ -278,6 +280,13 @@
+ (BOOL)isKeepingRawCssStyles:(NSString*)pageId;
++ (void)callUnicornRenderAction:(NSString*)instanceId
+ module:(const char*)module
+ method:(const char*)method
+ context:(JSContext*)context
+ args:(JSValueRef[])args
+ argCount:(int)argCount;
+
@end
#endif
diff --git a/ios/sdk/WeexSDK/Sources/Bridge/WXCoreBridge.mm b/ios/sdk/WeexSDK/Sources/Bridge/WXCoreBridge.mm
index 07db550..8ecba0f 100644
--- a/ios/sdk/WeexSDK/Sources/Bridge/WXCoreBridge.mm
+++ b/ios/sdk/WeexSDK/Sources/Bridge/WXCoreBridge.mm
@@ -300,12 +300,12 @@
if ([object isKindOfClass:[NSString class]]) {
returnValue->type = ParamsType::BYTEARRAYSTRING;
const char *pcstr_utf8 = [(NSString *)object UTF8String];
- returnValue->value.byteArray = generator_bytes_array(pcstr_utf8, ((NSString *)object).length);
+ returnValue->value.byteArray = generator_bytes_array(pcstr_utf8, [(NSString *)object lengthOfBytesUsingEncoding:NSUTF8StringEncoding]);
}
if ([object isKindOfClass:[NSDictionary class]] || [object isKindOfClass:[NSArray class]]) {
NSString *jsonString = [WXUtility JSONString:object];
returnValue->type = ParamsType::BYTEARRAYJSONSTRING;
- returnValue->value.byteArray = generator_bytes_array(jsonString.UTF8String, jsonString.length);
+ returnValue->value.byteArray = generator_bytes_array(jsonString.UTF8String, [jsonString lengthOfBytesUsingEncoding:NSUTF8StringEncoding]);
}
break;
}
@@ -1529,6 +1529,7 @@
static WeexCore::PlatformBridge* platformBridge = nullptr;
static WeexCore::ScriptBridge* jsBridge = nullptr;
+static UnicornRenderFunc unicornRenderFunction = nullptr;
+ (void)install
{
@@ -1559,6 +1560,9 @@
#else
weex::base::LogImplement::getLog()->setDebugMode(false);
#endif
+
+ Class UnicornRenderClass = NSClassFromString(@"UnicornRender");
+ unicornRenderFunction = [(id<WXUnicornRenderProtocol>)UnicornRenderClass getRenderFunc];
platformBridge = new WeexCore::PlatformBridge();
platformBridge->set_platform_side(new WeexCore::IOSSide());
@@ -1930,4 +1934,26 @@
return static_cast<RenderPage*>(page)->reserve_css_styles();
}
++ (void)callUnicornRenderAction:(NSString*)instanceId
+ module:(const char*)module
+ method:(const char*)method
+ context:(JSContext*)context
+ args:(JSValueRef[])args
+ argCount:(int)argCount {
+ std::unique_ptr<JSValueRef[]> values = std::unique_ptr<JSValueRef[]>(new JSValueRef[argCount]);
+ for (int i = 0; i < argCount; i ++) {
+ values[i] = args[i];
+ }
+ if (!unicornRenderFunction) {
+ Class UnicornRenderClass = NSClassFromString(@"UnicornRender");
+ unicornRenderFunction = [(id<WXUnicornRenderProtocol>)UnicornRenderClass getRenderFunc];
+ }
+ unicornRenderFunction([instanceId UTF8String],
+ module,
+ method,
+ context.JSGlobalContextRef,
+ values.get(),
+ argCount);
+}
+
@end
diff --git a/ios/sdk/WeexSDK/Sources/Bridge/WXJSCoreBridge.mm b/ios/sdk/WeexSDK/Sources/Bridge/WXJSCoreBridge.mm
index 413b6d9..15593fe 100644
--- a/ios/sdk/WeexSDK/Sources/Bridge/WXJSCoreBridge.mm
+++ b/ios/sdk/WeexSDK/Sources/Bridge/WXJSCoreBridge.mm
@@ -24,6 +24,7 @@
#import "WXDefine.h"
#import "WXUtility.h"
#import "WXSDKEngine.h"
+#import "WXSDKInstance_private.h"
#import "WXSDKError.h"
#import <sys/utsname.h>
#import <JavaScriptCore/JavaScriptCore.h>
@@ -38,6 +39,7 @@
#import "JSContext+Weex.h"
#import "WXCoreBridge.h"
#import "WXAnalyzerCenter.h"
+#import "WXSDKInstance_performance.h"
#import <dlfcn.h>
@@ -148,7 +150,7 @@
[[WXBridgeManager sharedManager].lastMethodInfo setObject:method ?: @"" forKey:@"method"];
[[WXBridgeManager sharedManager].lastMethodInfo setObject:args ?: @[] forKey:@"args"];
});
- return [[_jsContext globalObject] invokeMethod:method withArguments:args];
+ return [[_jsContext globalObject] invokeMethod:method withArguments:[args copy]];
}
- (void)registerCallNative:(WXJSCallNative)callNative
@@ -199,8 +201,19 @@
- (void)registerCallAddElement:(WXJSCallAddElement)callAddElement
{
id callAddElementBlock = ^(JSValue *instanceId, JSValue *ref, JSValue *element, JSValue *index, JSValue *ifCallback) {
-
NSString *instanceIdString = [instanceId toString];
+ WXSDKInstance *instance = [WXSDKManager instanceForID:instanceIdString];
+ if (instance.unicornRender) {
+ JSValueRef args[] = {instanceId.JSValueRef, ref.JSValueRef, element.JSValueRef, index.JSValueRef};
+ [WXCoreBridge callUnicornRenderAction:instanceIdString
+ module:"dom"
+ method:"addElement"
+ context:[JSContext currentContext]
+ args:args
+ argCount:4];
+ return [JSValue valueWithInt32:0 inContext:[JSContext currentContext]];
+ }
+
NSDictionary *componentData = [element toDictionary];
NSString *parentRef = [ref toString];
NSInteger insertIndex = [[index toNumber] integerValue];
@@ -216,8 +229,19 @@
- (void)registerCallCreateBody:(WXJSCallCreateBody)callCreateBody
{
id WXJSCallCreateBodyBlock = ^(JSValue *instanceId, JSValue *body,JSValue *ifCallback) {
-
NSString *instanceIdString = [instanceId toString];
+ WXSDKInstance *instance = [WXSDKManager instanceForID:instanceIdString];
+ if (instance.unicornRender) {
+ JSValueRef args[] = {instanceId.JSValueRef, body.JSValueRef};
+ [WXCoreBridge callUnicornRenderAction:instanceIdString
+ module:"dom"
+ method:"createBody"
+ context:[JSContext currentContext]
+ args:args
+ argCount:2];
+ return [JSValue valueWithInt32:0 inContext:[JSContext currentContext]];
+ }
+
NSDictionary *bodyData = [body toDictionary];
WXLogDebug(@"callCreateBody...%@, %@,", instanceIdString, bodyData);
@@ -232,6 +256,17 @@
id WXJSCallCreateBodyBlock = ^(JSValue *instanceId, JSValue *ref,JSValue *ifCallback) {
NSString *instanceIdString = [instanceId toString];
+ WXSDKInstance *instance = [WXSDKManager instanceForID:instanceIdString];
+ if (instance.unicornRender) {
+ JSValueRef args[] = {instanceId.JSValueRef, ref.JSValueRef};
+ [WXCoreBridge callUnicornRenderAction:instanceIdString
+ module:"dom"
+ method:"removeElement"
+ context:[JSContext currentContext]
+ args:args
+ argCount:2];
+ return [JSValue valueWithInt32:0 inContext:[JSContext currentContext]];
+ }
NSString *refString = [ref toString];
WXLogDebug(@"callRemoveElement...%@, %@,", instanceIdString, refString);
@@ -246,6 +281,18 @@
id WXJSCallMoveElementBlock = ^(JSValue *instanceId, JSValue *ref,JSValue *parentRef,JSValue *index, JSValue *ifCallback) {
NSString *instanceIdString = [instanceId toString];
+ WXSDKInstance *instance = [WXSDKManager instanceForID:instanceIdString];
+ if (instance.unicornRender) {
+ JSValueRef args[] = {instanceId.JSValueRef, ref.JSValueRef, parentRef.JSValueRef, index.JSValueRef};
+ [WXCoreBridge callUnicornRenderAction:instanceIdString
+ module:"dom"
+ method:"moveElement"
+ context:[JSContext currentContext]
+ args:args
+ argCount:4];
+ return [JSValue valueWithInt32:0 inContext:[JSContext currentContext]];
+ }
+
NSString *refString = [ref toString];
NSString *parentRefString = [parentRef toString];
NSInteger moveIndex = [[index toNumber] integerValue];
@@ -262,6 +309,17 @@
id WXJSCallUpdateAttrsBlock = ^(JSValue *instanceId, JSValue *ref,JSValue *attrs, JSValue *ifCallback) {
NSString *instanceIdString = [instanceId toString];
+ WXSDKInstance *instance = [WXSDKManager instanceForID:instanceIdString];
+ if (instance.unicornRender) {
+ JSValueRef args[] = {instanceId.JSValueRef, ref.JSValueRef, attrs.JSValueRef};
+ [WXCoreBridge callUnicornRenderAction:instanceIdString
+ module:"dom"
+ method:"updateAttributes"
+ context:[JSContext currentContext]
+ args:args
+ argCount:3];
+ return [JSValue valueWithInt32:0 inContext:[JSContext currentContext]];
+ }
NSString *refString = [ref toString];
NSDictionary *attrsData = [attrs toDictionary];
@@ -277,6 +335,17 @@
id WXJSCallUpdateStyleBlock = ^(JSValue *instanceId, JSValue *ref,JSValue *styles, JSValue *ifCallback) {
NSString *instanceIdString = [instanceId toString];
+ WXSDKInstance *instance = [WXSDKManager instanceForID:instanceIdString];
+ if (instance.unicornRender) {
+ JSValueRef args[] = {instanceId.JSValueRef, ref.JSValueRef, styles.JSValueRef};
+ [WXCoreBridge callUnicornRenderAction:instanceIdString
+ module:"dom"
+ method:"updateStyle"
+ context:[JSContext currentContext]
+ args:args
+ argCount:3];
+ return [JSValue valueWithInt32:0 inContext:[JSContext currentContext]];
+ }
NSString *refString = [ref toString];
NSDictionary *stylessData = [styles toDictionary];
@@ -292,6 +361,17 @@
id WXJSCallAddEventBlock = ^(JSValue *instanceId, JSValue *ref,JSValue *event, JSValue *ifCallback) {
NSString *instanceIdString = [instanceId toString];
+ WXSDKInstance *instance = [WXSDKManager instanceForID:instanceIdString];
+ if (instance.unicornRender) {
+ JSValueRef args[] = {instanceId.JSValueRef, ref.JSValueRef, event.JSValueRef};
+ [WXCoreBridge callUnicornRenderAction:instanceIdString
+ module:"dom"
+ method:"addEvent"
+ context:[JSContext currentContext]
+ args:args
+ argCount:3];
+ return [JSValue valueWithInt32:0 inContext:[JSContext currentContext]];
+ }
NSString *refString = [ref toString];
NSString *eventString = [event toString];
@@ -307,6 +387,17 @@
id WXJSCallRemoveEventBlock = ^(JSValue *instanceId, JSValue *ref,JSValue *event, JSValue *ifCallback) {
NSString *instanceIdString = [instanceId toString];
+ WXSDKInstance *instance = [WXSDKManager instanceForID:instanceIdString];
+ if (instance.unicornRender) {
+ JSValueRef args[] = {instanceId.JSValueRef, ref.JSValueRef, event.JSValueRef};
+ [WXCoreBridge callUnicornRenderAction:instanceIdString
+ module:"dom"
+ method:"removeEvent"
+ context:[JSContext currentContext]
+ args:args
+ argCount:3];
+ return [JSValue valueWithInt32:0 inContext:[JSContext currentContext]];
+ }
NSString *refString = [ref toString];
NSString *eventString = [event toString];
@@ -321,6 +412,31 @@
{
id WXJSCallCreateFinishBlock = ^(JSValue *instanceId, JSValue *ifCallback) {
NSString *instanceIdString = [instanceId toString];
+ WXSDKInstance *instance = [WXSDKManager instanceForID:instanceIdString];
+ if (instance.unicornRender) {
+ JSValueRef args[] = {instanceId.JSValueRef};
+ [WXCoreBridge callUnicornRenderAction:instanceIdString
+ module:"dom"
+ method:"createFinish"
+ context:[JSContext currentContext]
+ args:args
+ argCount:1];
+
+ WXPerformBlockOnMainThread(^{
+ WX_MONITOR_INSTANCE_PERF_END(WXPTFirstScreenRender, instance);
+ WX_MONITOR_INSTANCE_PERF_END(WXPTAllRender, instance);
+ WX_MONITOR_SUCCESS(WXMTJSBridge);
+ WX_MONITOR_SUCCESS(WXMTNativeRender);
+ [instance updatePerDicAfterCreateFinish];
+
+ UIView *rootView = instance.rootView;
+ [instance.performance onInstanceRenderSuccess:instance];
+ if (instance.renderFinish) {
+ instance.renderFinish(rootView);
+ }
+ });
+ return [JSValue valueWithInt32:0 inContext:[JSContext currentContext]];
+ }
WXLogDebug(@"callCreateFinish...%@", instanceIdString);
return [JSValue valueWithInt32:(int32_t)callCreateFinish(instanceIdString) inContext:[JSContext currentContext]];
};
@@ -378,6 +494,18 @@
{
_jsContext[@"callNativeComponent"] = ^void(JSValue *instanceId, JSValue *componentName, JSValue *methodName, JSValue *args, JSValue *options) {
NSString *instanceIdString = [instanceId toString];
+ WXSDKInstance *instance = [WXSDKManager instanceForID:instanceIdString];
+ if (instance.unicornRender) {
+ JSValueRef arguments[] = {instanceId.JSValueRef, componentName.JSValueRef, methodName.JSValueRef, args.JSValueRef};
+ [WXCoreBridge callUnicornRenderAction:instanceIdString
+ module:"dom"
+ method:"callNativeComponent"
+ context:[JSContext currentContext]
+ args:arguments
+ argCount:4];
+ return;
+ }
+
NSString *componentNameString = [componentName toString];
NSString *methodNameString = [methodName toString];
NSArray *argsArray = [args toArray];
diff --git a/ios/sdk/WeexSDK/Sources/Component/WXCellComponent.h b/ios/sdk/WeexSDK/Sources/Component/WXCellComponent.h
index 923af9d..3004fee 100644
--- a/ios/sdk/WeexSDK/Sources/Component/WXCellComponent.h
+++ b/ios/sdk/WeexSDK/Sources/Component/WXCellComponent.h
@@ -46,4 +46,6 @@
@property (nonatomic, weak) id<WXCellRenderDelegate> delegate;
@property (nonatomic, strong) NSString *zIndex;
+@property (nonatomic, assign) BOOL ignoreScrollSnap;
+
@end
diff --git a/ios/sdk/WeexSDK/Sources/Component/WXCellComponent.mm b/ios/sdk/WeexSDK/Sources/Component/WXCellComponent.mm
index 1a3b294..4b8098c 100644
--- a/ios/sdk/WeexSDK/Sources/Component/WXCellComponent.mm
+++ b/ios/sdk/WeexSDK/Sources/Component/WXCellComponent.mm
@@ -61,6 +61,10 @@
//in Android, cellReuse is not equal cellDataRecycle
[weexInstance.apmInstance updateDiffStats:KEY_PAGE_STATS_CELL_DATA_UN_RECYCLE_NUM withDiffValue:1];
}
+ if (attributes[@"scrollSnapIgnore"]) {
+ _ignoreScrollSnap = [WXConvert BOOL:attributes[@"scrollSnapIgnore"]];
+ }
+
}
return self;
@@ -106,6 +110,10 @@
if (attributes[@"keepScrollPosition"]) {
_keepScrollPosition = [WXConvert BOOL:attributes[@"keepScrollPosition"]];
}
+
+ if (attributes[@"scrollSnapIgnore"]) {
+ _ignoreScrollSnap = [WXConvert BOOL:attributes[@"scrollSnapIgnore"]];
+ }
}
- (void)_moveToSupercomponent:(WXComponent *)newSupercomponent atIndex:(NSUInteger)index
diff --git a/ios/sdk/WeexSDK/Sources/Component/WXComponent_internal.h b/ios/sdk/WeexSDK/Sources/Component/WXComponent_internal.h
index dee6a8c..ec61ffd 100644
--- a/ios/sdk/WeexSDK/Sources/Component/WXComponent_internal.h
+++ b/ios/sdk/WeexSDK/Sources/Component/WXComponent_internal.h
@@ -98,6 +98,8 @@
UILongPressGestureRecognizer *_longPressGesture;
UIPanGestureRecognizer *_panGesture;
+ BOOL _enableScreenEdgePanGesture;
+
BOOL _cancelsTouchesInView;
BOOL _listenPanStart;
diff --git a/ios/sdk/WeexSDK/Sources/Component/WXListComponent.mm b/ios/sdk/WeexSDK/Sources/Component/WXListComponent.mm
index 7b31e26..1670498 100644
--- a/ios/sdk/WeexSDK/Sources/Component/WXListComponent.mm
+++ b/ios/sdk/WeexSDK/Sources/Component/WXListComponent.mm
@@ -171,6 +171,9 @@
BOOL _isUpdating;
NSMutableArray<void(^)(void)> *_updates;
NSTimeInterval _reloadInterval;
+
+ CGPoint *_targetContentOffset;
+ CGPoint _nextStepContentOffset;
}
- (instancetype)initWithRef:(NSString *)ref type:(NSString *)type styles:(NSDictionary *)styles attributes:(NSDictionary *)attributes events:(NSArray *)events weexInstance:(WXSDKInstance *)weexInstance
@@ -282,6 +285,18 @@
contentOffsetY += cellRect.origin.y;
contentOffsetY += offset * self.weexInstance.pixelScaleFactor;
+ if (self.snapData.useSnap) {
+ CGFloat snapOffset = [self.snapData calcScrollSnapPositionOffset];
+ contentOffsetY -= (offset * self.weexInstance.pixelScaleFactor + snapOffset);
+ if (self.snapData.alignment == WXScrollSnapAlignCenter) {
+ contentOffsetY += (cellRect.size.height / 2);
+ } else if (self.snapData.alignment == WXScrollSnapAlignEnd) {
+ contentOffsetY += cellRect.size.height;
+ }
+ if (contentOffsetY < 0) {
+ contentOffsetY = 0;
+ }
+ }
if (_tableView.contentSize.height >= _tableView.frame.size.height && contentOffsetY > _tableView.contentSize.height - _tableView.frame.size.height) {
contentOffset.y = _tableView.contentSize.height - _tableView.frame.size.height;
} else {
@@ -623,7 +638,8 @@
NSIndexPath *fromIndexPath = [self indexPathForCell:cell sections:_sections];
NSIndexPath *toIndexPath = [self indexPathForSubIndex:index];
if (toIndexPath.row > [_sections[toIndexPath.section].rows count] || toIndexPath.row < 0) {
- WXLogError(@"toIndexPath %@ is out of range as the current is %lu",toIndexPath ,(unsigned long)[_sections[toIndexPath.section].rows count]);
+ //FIXME: WXLogError trigger crash
+// WXLogError(@"toIndexPath %@ is out of range as the current is %lu",toIndexPath ,(unsigned long)[_sections[toIndexPath.section].rows count]);
return;
}
[self removeCellForIndexPath:fromIndexPath withSections:_sections];
@@ -728,6 +744,140 @@
}
}
+- (NSIndexPath *)getNeighbouringIndexPath:(NSIndexPath *)currentIndexPath findNext:(BOOL)findNext {
+ NSIndexPath *neighbourIndexPath;
+ if (findNext) {
+ if (currentIndexPath.row == _completedSections[currentIndexPath.section].rows.count - 1) {
+ if (currentIndexPath.section == _completedSections.count-1) {// the last cell
+ neighbourIndexPath = [NSIndexPath indexPathForRow:currentIndexPath.row inSection:currentIndexPath.section];
+ } else {// the first cell on next section
+ neighbourIndexPath = [NSIndexPath indexPathForRow:0 inSection:currentIndexPath.section+1];
+ }
+ } else {// the next cell
+ neighbourIndexPath = [NSIndexPath indexPathForRow:currentIndexPath.row+1 inSection:currentIndexPath.section];
+ }
+ } else {
+ if (currentIndexPath.row == 0) {
+ if (currentIndexPath.section == 0) {// the first cell
+ neighbourIndexPath = [NSIndexPath indexPathForRow:0 inSection:0];
+ } else {// the last cell on previous section
+ neighbourIndexPath = [NSIndexPath indexPathForRow:_completedSections[currentIndexPath.section-1].rows.count-1 inSection:currentIndexPath.section-1];
+ }
+ } else {// the previous cell
+ neighbourIndexPath = [NSIndexPath indexPathForRow:currentIndexPath.row-1 inSection:currentIndexPath.section];
+ }
+ }
+ return neighbourIndexPath;
+}
+
+
+- (CGPoint)calculateSnapPosition:(UIScrollView *)scrollView withVelocity:(CGPoint)velocity startPosition:(CGPoint)startPosition targetPosition:(CGPoint)preTargetPosition{
+ [self.snapData bindingScrollView:scrollView];
+ WXTableView *tableView = (WXTableView *)scrollView;
+ /// The offset for start position, to avoid the start position error in continuous sliding if last sliding was not finished
+ CGFloat kstartPositionOffset = 10.f;
+
+ /// The snap point of scroll container when finger touch down
+ self.snapData.startPosition = startPosition;
+ CGPoint snapContainerPosition, currentOffset;
+ CGPoint currentPoint = scrollView.contentOffset;
+ /// The offset of the snap point relative to the container vertex
+ CGFloat snapOffset = [self.snapData calcScrollSnapPositionOffset];
+ if (self.scrollDirection == WXScrollDirectionHorizontal) {
+ currentOffset = CGPointMake(currentPoint.x-startPosition.x, currentPoint.y);
+ snapContainerPosition = CGPointMake(startPosition.x + snapOffset, startPosition.y);
+ } else {
+ currentOffset = CGPointMake(currentPoint.x, currentPoint.y-startPosition.y);
+ snapContainerPosition = CGPointMake(startPosition.x, startPosition.y + snapOffset);
+ }
+ /// Calculate snap staus
+ WXScrollSnapStatus snapStatus = [self.snapData shouldTriggerSnap:currentOffset velocity:velocity];
+
+ CGPoint targetContentOffset = startPosition;
+ if (currentPoint.x < 0 || currentPoint.y < 0) {
+ return targetContentOffset;
+ }
+ /// Bounce to origin offset
+ if (snapStatus == WXScrollSnapNone) {
+ return targetContentOffset;
+ }
+
+ /// Determine the start position, if align-start + 4, else if align-end - 4
+ CGPoint correctSnapPosition = snapContainerPosition;
+ if (self.scrollDirection == WXScrollDirectionHorizontal) {
+ switch (self.snapData.alignment) {
+ case WXScrollSnapAlignStart:
+ correctSnapPosition.x += kstartPositionOffset;
+ break;
+ case WXScrollSnapAlignEnd:
+ correctSnapPosition.x -= kstartPositionOffset;
+ break;
+ default:
+ break;
+ }
+ } else {
+ switch (self.snapData.alignment) {
+ case WXScrollSnapAlignStart:
+ correctSnapPosition.y += kstartPositionOffset;
+ break;
+ case WXScrollSnapAlignEnd:
+ correctSnapPosition.y -= kstartPositionOffset;
+ break;
+ default:
+ break;
+ }
+ }
+ /// The cell corresponding to the starting point
+ NSIndexPath *beginIndexPath = [tableView indexPathForRowAtPoint:correctSnapPosition];
+ CGRect beginCellRect = [tableView rectForRowAtIndexPath:beginIndexPath];
+ if (CGRectIsNull(beginCellRect)) {
+ return targetContentOffset;
+ }
+ NSIndexPath *targetIndexPath = beginIndexPath;
+
+ if (snapStatus == WXScrollSnapStay) {
+ // Do nothing
+ } else {
+ NSIndexPath *lastIndexPath = beginIndexPath;
+ targetIndexPath = [self getNeighbouringIndexPath:beginIndexPath findNext:(snapStatus == WXScrollSnapToNext)];
+ WXCellComponent *cell = [self cellForIndexPath:targetIndexPath];
+ while (cell.ignoreScrollSnap && [lastIndexPath compare:targetIndexPath] != NSOrderedSame) {
+ lastIndexPath = targetIndexPath;
+ targetIndexPath = [self getNeighbouringIndexPath:targetIndexPath findNext:(snapStatus == WXScrollSnapToNext)];
+ cell = [self cellForIndexPath:targetIndexPath];
+ }
+ }
+
+ CGRect targetCellRect = [tableView rectForRowAtIndexPath:targetIndexPath];
+
+ if (self.scrollDirection == WXScrollDirectionVertical) {
+ targetContentOffset.y = targetCellRect.origin.y - snapOffset;
+ if (self.snapData.alignment == WXScrollSnapAlignCenter) {
+ targetContentOffset.y += (targetCellRect.size.height / 2);
+ } else if (self.snapData.alignment == WXScrollSnapAlignEnd) {
+ targetContentOffset.y += targetCellRect.size.height;
+ }
+ if (targetContentOffset.y < 0) {
+ targetContentOffset.y = 0;
+ }
+ } else {
+ targetContentOffset.x = targetCellRect.origin.x - snapOffset;
+ if (self.snapData.alignment == WXScrollSnapAlignCenter) {
+ targetContentOffset.x += (targetCellRect.size.width / 2);
+ } else if (self.snapData.alignment == WXScrollSnapAlignEnd) {
+ targetContentOffset.x += targetCellRect.size.width;
+ }
+ if (targetContentOffset.x < 0) {
+ targetContentOffset.x = 0;
+ }
+ }
+ WXLogInfo(@"[scroll snap] veloc:%.2f (%.2f,%.2f)=>(%.2f,%.2f)", velocity.y, startPosition.x, startPosition.y, targetContentOffset.x, targetContentOffset.y);
+ self.snapData.targetIndexPath = targetIndexPath;
+ self.snapData.snapping = true;
+
+ return targetContentOffset;
+}
+
- (void)scrollViewDidScroll:(UIScrollView *)scrollView
{
[super scrollViewDidScroll:scrollView];
diff --git a/ios/sdk/WeexSDK/Sources/Component/WXRichText.mm b/ios/sdk/WeexSDK/Sources/Component/WXRichText.mm
index d007d60..cfbbea6 100644
--- a/ios/sdk/WeexSDK/Sources/Component/WXRichText.mm
+++ b/ios/sdk/WeexSDK/Sources/Component/WXRichText.mm
@@ -28,6 +28,7 @@
#import "WXComponentManager.h"
#import "WXLog.h"
#import "WXDarkSchemeProtocol.h"
+#import "WXAssert.h"
#include <pthread/pthread.h>
@interface WXRichNode : NSObject
@@ -80,7 +81,6 @@
self.accessibilityTraits |= UIAccessibilityTraitStaticText;
self.opaque = NO;
self.editable = NO;
- self.selectable = YES;
self.contentMode = UIViewContentModeRedraw;
self.textContainerInset = UIEdgeInsetsZero;
self.textContainer.lineFragmentPadding = 0.0f;
@@ -126,6 +126,7 @@
pthread_mutex_t _attributedStringMutex;
pthread_mutexattr_t _propertMutexAttr;
CGFloat _lineHeight;
+ BOOL _selectable;
}
- (void)dealloc
@@ -140,6 +141,7 @@
textView = [[WXRichTextView alloc]init];
textView.delegate = self;
textView.scrollEnabled = NO;
+ textView.selectable = _selectable;
}
return textView;
}
@@ -162,6 +164,10 @@
pthread_mutexattr_init(&(_propertMutexAttr));
pthread_mutexattr_settype(&(_propertMutexAttr), PTHREAD_MUTEX_RECURSIVE);
pthread_mutex_init(&(_attributedStringMutex), &(_propertMutexAttr));
+ _selectable = YES;
+ if (_attributes[@"selectable"]) {
+ _selectable = [WXConvert BOOL:_attributes[@"selectable"]];
+ }
}
return self;
}
@@ -595,10 +601,24 @@
}
- (void)updateAttributes:(NSDictionary *)attributes {
+ WXAssertMainThread();
+
+ if (attributes[@"selectable"]) {
+ _selectable = [WXConvert BOOL:attributes[@"selectable"]];
+ }
+ [self textView].selectable = _selectable;
+
+ __weak WXRichText* weakSelf = self;
WXPerformBlockOnComponentThread(^{
- _attributes = [NSMutableDictionary dictionaryWithDictionary:attributes];
- [self syncTextStorageForView];
+ __strong WXRichText* strongSelf = weakSelf;
+ if (strongSelf == nil) {
+ return;
+ }
+ strongSelf->_attributes = [NSMutableDictionary dictionaryWithDictionary:attributes];
+ [strongSelf syncTextStorageForView];
});
+
+
}
- (void)updateChildNodeAttributes:(NSDictionary *)attributes ref:(NSString*)ref parentRef:(NSString*)parentRef {
diff --git a/ios/sdk/WeexSDK/Sources/Component/WXScrollerComponent.h b/ios/sdk/WeexSDK/Sources/Component/WXScrollerComponent.h
index 687aa80..f12d2ba 100644
--- a/ios/sdk/WeexSDK/Sources/Component/WXScrollerComponent.h
+++ b/ios/sdk/WeexSDK/Sources/Component/WXScrollerComponent.h
@@ -22,6 +22,8 @@
NS_ASSUME_NONNULL_BEGIN
+@class WXScrollSnapData, WXScrollAnimator;
+
@interface WXScrollerComponent : WXComponent <WXScrollerProtocol, UIScrollViewDelegate>
@property (nonatomic, copy) void (^onScroll)(UIScrollView *scrollView);
@@ -32,10 +34,114 @@
@property (nonatomic, assign) CGSize contentSize;
+@property (nonatomic, strong) WXScrollSnapData *snapData;
+
+@property (nonatomic, strong) WXScrollAnimator * _Nullable scrollAnimator;
+
- (void)handleAppear;
- (CGPoint)absolutePositionForComponent:(WXComponent *)component;
@end
+typedef NS_ENUM(NSUInteger, WXScrollSnapStatus) {
+ WXScrollSnapToNext,
+ WXScrollSnapToPrev,
+ WXScrollSnapStay,
+ WXScrollSnapNone,
+};
+
+typedef NS_ENUM(NSUInteger, WXScrollSnapAlignment) {
+ WXScrollSnapAlignStart,
+ WXScrollSnapAlignCenter,
+ WXScrollSnapAlignEnd,
+ WXScrollSnapAlignNone,
+};
+
+typedef NS_ENUM(NSUInteger, WXScrollAnimateFunction) {
+ WXScrollAnimateNone = 0,
+ WXScrollAnimateLinear,
+ WXScrollAnimateQuadOut,
+ WXScrollAnimateQuadInOut,
+ WXScrollAnimateQuadIn,
+ WXScrollAnimateCubicIn,
+ WXScrollAnimateCubicOut,
+ WXScrollAnimateCubicInOut,
+ WXScrollAnimateQuartIn,
+ WXScrollAnimateQuartOut,
+ WXScrollAnimateQuartInOut,
+ WXScrollAnimateSineIn,
+ WXScrollAnimateSineOut,
+ WXScrollAnimateSineInOut,
+ WXScrollAnimateQuintIn,
+ WXScrollAnimateQuintOut,
+ WXScrollAnimateQuintInOut,
+ WXScrollAnimateExpoIn,
+ WXScrollAnimateExpoOut,
+ WXScrollAnimateExpoInOut,
+ WXScrollAnimateCircleIn,
+ WXScrollAnimateCircleOut,
+ WXScrollAnimateCircleInOut,
+};
+
+@interface WXScrollSnapData : NSObject
+
+@property (nonatomic, assign) BOOL useSnap;
+
+@property (nonatomic, assign) WXScrollDirection axis;
+
+@property (nonatomic, assign) WXScrollSnapAlignment alignment;
+
+@property (nonatomic, assign) UIEdgeInsets padding;
+
+@property (nonatomic, strong) NSIndexPath *targetIndexPath;
+
+@property (nonatomic, weak) UIScrollView *scrollView;
+
+@property (nonatomic, assign) CGPoint startPosition;
+
+@property (nonatomic, assign) CGPoint targetPosition;
+
+@property (nonatomic, assign) BOOL snapping;
+
+/// scroll animate
+@property (nonatomic, assign) WXScrollAnimateFunction timingFunction;
+@property (nonatomic, assign) CGFloat scrollAnimateDuration;
+
+/// bind scrollView
+- (void)bindingScrollView:(UIScrollView *)scrollView;
+/// calc snap status;
+- (WXScrollSnapStatus)shouldTriggerSnap:(CGPoint)offset velocity:(CGPoint)velocity;
+///
+- (CGFloat)calcScrollSnapPositionOffset;
+
+@end
+
+typedef void(^WXScrollAnimatorCompletion)(void);
+
+@interface WXScrollAnimator : NSObject
+
+@property (nonatomic, weak) UIScrollView *scrollView;
+
+@property (nonatomic, assign) WXScrollAnimateFunction timingFunction;
+
+@property (nonatomic, assign) NSTimeInterval startTime;
+@property (nonatomic, assign) NSTimeInterval duration;
+@property (nonatomic, assign) NSTimeInterval runTime;
+
+@property (nonatomic, assign) CGPoint startOffset;
+@property (nonatomic, assign) CGPoint destinationOffset;
+
+@property (nonatomic, copy) WXScrollAnimatorCompletion completion;
+
+@property (nonatomic, strong) CADisplayLink *timer;
+
+- (instancetype)initWithScrollView:(UIScrollView *)scrollView timingFunction:(WXScrollAnimateFunction)timingFunction;
+
+- (void)setContentOffset:(CGPoint)offset duration:(NSTimeInterval)duration;
+
+- (CGFloat)computeAnimateWithTime:(CGFloat)time begin:(CGFloat)begin change:(CGFloat)change duration:(CGFloat)duration;
+
+@end
+
NS_ASSUME_NONNULL_END
diff --git a/ios/sdk/WeexSDK/Sources/Component/WXScrollerComponent.mm b/ios/sdk/WeexSDK/Sources/Component/WXScrollerComponent.mm
index 981a7af..84225fa 100644
--- a/ios/sdk/WeexSDK/Sources/Component/WXScrollerComponent.mm
+++ b/ios/sdk/WeexSDK/Sources/Component/WXScrollerComponent.mm
@@ -84,6 +84,285 @@
@end
+
+@interface WXScrollSnapData ()
+
+@property (nonatomic, assign) CGPoint triggerOffset;
+
+@end
+
+/// Default snap trigger velocity and offset
+CGFloat kDefaultScrollSnapVelocity = 1.2;
+CGFloat kDefaultScrollSnapTriggerOffset = 60;
+
+@implementation WXScrollSnapData
+
+- (instancetype)init
+{
+ self = [super init];
+ if (self) {
+ self.triggerOffset = CGPointMake(kDefaultScrollSnapTriggerOffset, kDefaultScrollSnapTriggerOffset);
+ self.useSnap = false;
+ self.padding = UIEdgeInsetsZero;
+ self.scrollAnimateDuration = 0.25;
+ self.timingFunction = WXScrollAnimateNone;
+ }
+ return self;
+}
+
+- (void)bindingScrollView:(UIScrollView *)scrollView {
+ _scrollView = scrollView;
+ _scrollView.pagingEnabled = NO;
+ _scrollView.decelerationRate = UIScrollViewDecelerationRateFast;
+}
+
+- (WXScrollSnapStatus)shouldTriggerSnap:(CGPoint)offset velocity:(CGPoint)velocity {
+ BOOL shouldScrollToNextCell = false;
+ BOOL shouldScrollToPrevCell = false;
+
+ if (self.axis == WXScrollDirectionVertical) {
+ if (velocity.y > kDefaultScrollSnapVelocity) {
+ shouldScrollToNextCell = true;
+ } else if (velocity.y < -kDefaultScrollSnapVelocity) {
+ shouldScrollToPrevCell = true;
+ } else if (offset.y > self.triggerOffset.y) {
+ shouldScrollToNextCell = true;
+ } else if (offset.y < -(self.triggerOffset.y * 2)) {
+ shouldScrollToPrevCell = true;
+ }
+ } else {
+ if (velocity.x > kDefaultScrollSnapVelocity) {
+ shouldScrollToNextCell = true;
+ } else if (velocity.x < -kDefaultScrollSnapVelocity) {
+ shouldScrollToPrevCell = true;
+ } else if (offset.x > self.triggerOffset.x) {
+ shouldScrollToNextCell = true;
+ } else if (offset.x < -(self.triggerOffset.x * 2)) {
+ shouldScrollToPrevCell = true;
+ }
+ }
+ if (shouldScrollToPrevCell == false && shouldScrollToNextCell == false) {
+ return WXScrollSnapStay;
+ }
+ if (shouldScrollToNextCell == true && shouldScrollToPrevCell == true) {
+ return WXScrollSnapNone;
+ }
+ return shouldScrollToNextCell ? WXScrollSnapToNext : WXScrollSnapToPrev;
+}
+
+- (CGFloat)calcScrollSnapPositionOffset {
+ CGFloat targetOffset = 0;
+ if (!self.scrollView) {
+ return targetOffset;
+ }
+ CGSize containerSize = self.scrollView.frame.size;
+ if (self.axis == WXScrollDirectionHorizontal) {
+ switch (self.alignment) {
+ case WXScrollSnapAlignStart:
+ targetOffset = self.padding.left;
+ break;
+ case WXScrollSnapAlignCenter:
+ targetOffset = (containerSize.width + self.padding.left - self.padding.right)/2;
+ break;
+ case WXScrollSnapAlignEnd:
+ targetOffset = containerSize.width - self.padding.right;
+ break;
+ default:
+ targetOffset = self.padding.left;
+ break;
+ }
+ if (targetOffset < 0) {
+ targetOffset = 0;
+ }
+ if (targetOffset > self.scrollView.contentSize.width - containerSize.width) {
+ targetOffset = self.scrollView.contentSize.width - containerSize.width;
+ }
+ } else {
+ switch (self.alignment) {
+ case WXScrollSnapAlignStart:
+ targetOffset = self.padding.top;
+ break;
+ case WXScrollSnapAlignCenter:
+ targetOffset = (containerSize.height + self.padding.top - self.padding.bottom)/2;
+ break;
+ case WXScrollSnapAlignEnd:
+ targetOffset = containerSize.height - self.padding.bottom;
+ break;
+ default:
+ targetOffset = self.padding.top;
+ break;
+ }
+ if (targetOffset < 0) {
+ targetOffset = 0;
+ }
+ if (targetOffset > self.scrollView.contentSize.height - containerSize.height) {
+ targetOffset = self.scrollView.contentSize.height - containerSize.height;
+ }
+ }
+ return targetOffset;
+}
+
+
+- (NSString *)description
+{
+ return [NSString stringWithFormat:@"[ScrollSnap] {\n\tuseSnap:%d \n\tpadding:(top:%.2f, right:%.2f, bottom:%.2f, left:%.2f)\n\tstartPosition:(%.2f, %.2f)\n\ttargetIndexPath:%@\n\ttargetPosition(%.2f, %.2f)}", _useSnap, _padding.top, _padding.right, _padding.bottom, _padding.left ,_startPosition.x, _startPosition.y, _targetIndexPath, _targetPosition.x, _targetPosition.y];
+}
+
+@end
+
+@interface WXScrollAnimator()
+
+@end
+
+@implementation WXScrollAnimator
+
+- (instancetype)initWithScrollView:(WXScrollerComponentView *)scrollView timingFunction:(WXScrollAnimateFunction)timingFunction {
+ if (self = [super init]) {
+ _scrollView = scrollView;
+ _timingFunction = timingFunction;
+ }
+ return self;
+}
+
+- (CGFloat)computeAnimateWithTime:(CGFloat)time begin:(CGFloat)begin change:(CGFloat)change duration:(CGFloat)duration {
+ switch (self.timingFunction) {
+ case WXScrollAnimateNone:
+ return 0;
+ case WXScrollAnimateLinear:
+ return change * time / duration + begin;
+ case WXScrollAnimateQuadOut:
+ time /= duration;
+ return -change * time * (time - 2) + begin;
+ case WXScrollAnimateQuadInOut:
+ time /= (duration / 2);
+ if (time < 1) {
+ return change / 2 * time * time + begin;
+ }
+ time -= 1;
+ return -change / 2 * (time * (time - 2) - 1) + begin;
+ case WXScrollAnimateQuadIn:
+ time /= duration;
+ return change * time * time + begin;
+ case WXScrollAnimateCubicIn:
+ time /= duration;
+ return change * time * time * time + begin;
+ case WXScrollAnimateCubicOut:
+ time = time / duration - 1;
+ return change * (time * time * time + 1) + begin;
+ case WXScrollAnimateCubicInOut:
+ time /= duration / 2;
+ if (time < 1) {
+ return change / 2 * time * time * time + begin;
+ }
+ time -= 2;
+ return change / 2 * ( time * time * time + 2) + begin;
+ case WXScrollAnimateQuartIn:
+ time /= duration;
+ return change * time * time * time * time + begin;
+ case WXScrollAnimateQuartOut:
+ time = time / duration - 1;
+ return -change * (time * time * time * time - 1) + begin;
+ case WXScrollAnimateQuartInOut:
+ time /= duration / 2;
+ if (time < 1) {
+ return change / 2 * time * time * time * time + begin;
+ }
+ time -= 2;
+ return -change / 2 * (time * time * time * time - 2) + begin;
+ case WXScrollAnimateSineIn:
+ return -change * cos(time / duration * M_PI_2) + change + begin;
+ case WXScrollAnimateSineOut:
+ return change * sin(time / duration * M_PI_2) + begin;
+ case WXScrollAnimateSineInOut:
+ return -change / 2 * (cos(M_PI * time / duration) - 1) + begin;
+ case WXScrollAnimateQuintIn:
+ time /= duration;
+ return change * time * time * time * time * time + begin;
+ case WXScrollAnimateQuintOut:
+ time = time / duration - 1;
+ return change * (time * time * time * time * time + 1) + begin;
+ case WXScrollAnimateQuintInOut:
+ time /= duration / 2;
+ if (time < 1) {
+ return change / 2 * time * time * time * time * time + begin;
+ }
+ time -= 2;
+ return change / 2 * (time * time * time * time * time + 2) + begin;
+ case WXScrollAnimateExpoIn:
+ return (time == 0) ? begin : change * pow(2, 10*(time / duration - 1)) + begin;
+ case WXScrollAnimateExpoOut:
+ return (time == duration) ? begin + change : change * (-pow(2, -10 * time / duration) + 1) + begin;
+ case WXScrollAnimateExpoInOut:
+ if (time == 0) {
+ return begin;
+ }
+ if (time == duration) {
+ return begin + change;
+ }
+ time /= duration / 2;
+ if (time < 1) {
+ return change / 2 * pow(2, 10 * (time - 1)) + begin;
+ }
+ time -= 1;
+ return change / 2 * (-pow(2, -10 * time) + 2) + begin;
+ case WXScrollAnimateCircleIn:
+ time /= duration;
+ return -change * (sqrt(1 - time * time) - 1) + begin;
+ case WXScrollAnimateCircleOut:
+ time = time / duration - 1;
+ return change * sqrt(1 - time * time) + begin;
+ case WXScrollAnimateCircleInOut:
+ time /= duration / 2;
+ if (time < 1) {
+ return -change / 2 * (sqrt(1 - time * time) - 1) + begin;
+ }
+ time -= 2;
+ return change / 2 * (sqrt(1 - time * time) + 1) + begin;
+ }
+}
+
+- (void)setContentOffset:(CGPoint)offset duration:(NSTimeInterval)duration {
+ if (!_scrollView) {
+ return;
+ }
+ _startTime = [[NSDate date] timeIntervalSince1970];
+ _startOffset = _scrollView.contentOffset;
+ _destinationOffset = offset;
+ _duration = duration;
+ _runTime = 0;
+ if (_duration < 0) {
+ [_scrollView setContentOffset:offset animated:false];
+ return;
+ }
+ if (_timer == nil) {
+ _timer = [CADisplayLink displayLinkWithTarget:self selector:@selector(animatedScroll)];
+ [_timer addToRunLoop:[NSRunLoop mainRunLoop] forMode:NSRunLoopCommonModes];
+ }
+}
+
+- (void)animatedScroll {
+ if (!_timer || !_scrollView) {
+ return;
+ }
+ _runTime += _timer.duration;
+ if (_runTime >= _duration) {
+ [_scrollView setContentOffset:_destinationOffset animated:false];
+ [_timer invalidate];
+ _timer = nil;
+ if (self.completion) {
+ self.completion();
+ }
+ return;
+ }
+ CGPoint offset = _scrollView.contentOffset;
+ offset.x = [self computeAnimateWithTime:_runTime begin:_startOffset.x change:(_destinationOffset.x - _startOffset.x) duration:_duration];
+ offset.y = [self computeAnimateWithTime:_runTime begin:_startOffset.y change:(_destinationOffset.y - _startOffset.y) duration:_duration];
+ [_scrollView setContentOffset:offset animated:false];
+}
+
+@end
+
+
@interface WXScrollerComponent()
@property (nonatomic, strong) NSMutableArray * stickyArray;
@@ -176,6 +455,7 @@
_stickyArray = [NSMutableArray array];
_listenerArray = [NSMutableArray array];
+ _snapData = [[WXScrollSnapData alloc] init];
_scrollEvent = NO;
_scrollStartEvent = NO;
_scrollEndEvent = NO;
@@ -231,11 +511,83 @@
if (weexInstance.instanceCallback) {
weexInstance.instanceCallback(weexInstance, WXScrollerComponentCreatedCallback, self);
}
+
+ [self handleScrollSnapAttributes:attributes];
}
return self;
}
+- (void)handleScrollSnapAttributes:(NSDictionary *)attrs {
+ if (attrs[@"scrollSnap"]) {
+ _snapData.useSnap = attrs[@"scrollSnap"] ? [WXConvert BOOL:attrs[@"scrollSnap"]] : NO;
+ _snapData.axis = _scrollDirection;
+ }
+ CGFloat top=0, right=0, bottom=0, left=0;
+ if (attrs[@"scrollPaddingTop"]) {
+ top = attrs[@"scrollPaddingTop"] ? [WXConvert WXPixelType:attrs[@"scrollPaddingTop"] scaleFactor:self.weexInstance.pixelScaleFactor] : 0.f;
+ }
+ if (attrs[@"scrollPaddingRight"]) {
+ right = attrs[@"scrollPaddingRight"] ? [WXConvert WXPixelType:attrs[@"scrollPaddingRight"] scaleFactor:self.weexInstance.pixelScaleFactor] : 0.f;
+ }
+ if (attrs[@"scrollPaddingBottom"]) {
+ bottom = attrs[@"scrollPaddingBottom"] ? [WXConvert WXPixelType:attrs[@"scrollPaddingBottom"] scaleFactor:self.weexInstance.pixelScaleFactor] : 0.f;
+ }
+ if (attrs[@"scrollPaddingLeft"]) {
+ left = attrs[@"scrollPaddingLeft"] ? [WXConvert WXPixelType:attrs[@"scrollPaddingLeft"] scaleFactor:self.weexInstance.pixelScaleFactor] : 0.f;
+ }
+ _snapData.padding = UIEdgeInsetsMake(top, left, bottom, right);
+ if (attrs[@"scrollSnapAlign"]) {
+ NSString *alignment = attrs[@"scrollSnapAlign"];
+ if ([alignment isEqualToString:@"start"]) {
+ _snapData.alignment = WXScrollSnapAlignStart;
+ } else if ([alignment isEqualToString:@"center"]) {
+ _snapData.alignment = WXScrollSnapAlignCenter;
+ } else if ([alignment isEqualToString:@"end"]) {
+ _snapData.alignment = WXScrollSnapAlignEnd;
+ } else {
+ _snapData.alignment = WXScrollSnapAlignNone;
+ }
+ }
+ if (attrs[@"scrollAnimateFunc"]) {
+ _snapData.timingFunction = [self translateScrollAnimateFunction:attrs[@"scrollAnimateFunc"]];
+ }
+ if (attrs[@"scrollAnimateDuration"]) {
+ _snapData.scrollAnimateDuration = [WXConvert CGFloat:attrs[@"scrollAnimateDuration"]];
+ }
+}
+
+- (WXScrollAnimateFunction)translateScrollAnimateFunction:(NSString *)name {
+ NSDictionary *dic = @{
+ @"linear" : @(WXScrollAnimateLinear),
+ @"quadIn" : @(WXScrollAnimateQuadIn),
+ @"quadOut" : @(WXScrollAnimateQuadOut),
+ @"quadInOut" : @(WXScrollAnimateQuadInOut),
+ @"cubicIn" : @(WXScrollAnimateCubicIn),
+ @"cubicOut" : @(WXScrollAnimateCubicOut),
+ @"cubicInOut" : @(WXScrollAnimateCubicInOut),
+ @"quartIn" : @(WXScrollAnimateQuartIn),
+ @"quartOut" : @(WXScrollAnimateQuartOut),
+ @"quartInOut" : @(WXScrollAnimateQuartInOut),
+ @"quintIn" : @(WXScrollAnimateQuintIn),
+ @"quintOut" : @(WXScrollAnimateQuintOut),
+ @"quintInOut" : @(WXScrollAnimateQuintInOut),
+ @"sineIn" : @(WXScrollAnimateSineIn),
+ @"sineOut" : @(WXScrollAnimateSineOut),
+ @"sineInOut" : @(WXScrollAnimateSineInOut),
+ @"expoIn" : @(WXScrollAnimateExpoIn),
+ @"expoOut" : @(WXScrollAnimateExpoOut),
+ @"expoInOut" : @(WXScrollAnimateExpoInOut),
+ @"circleIn" : @(WXScrollAnimateCircleIn),
+ @"circleOut" : @(WXScrollAnimateCircleOut),
+ @"circleInOut" : @(WXScrollAnimateCircleInOut),
+ };
+ if (dic[name]) {
+ return (WXScrollAnimateFunction)[dic[name] integerValue];
+ }
+ return WXScrollAnimateNone;
+}
+
- (UIView *)loadView
{
return [[WXScrollerComponentView alloc] init];
@@ -278,9 +630,10 @@
scrollView.scrollsToTop = YES;
}
- if (_pagingEnabled && _pageSize > 0) {
+ if ((_pagingEnabled && _pageSize > 0) || _snapData.useSnap) {
scrollView.pagingEnabled = NO; // turn off system default paging
scrollView.decelerationRate = UIScrollViewDecelerationRateFast;
+ [_snapData bindingScrollView:scrollView];
}
else {
scrollView.decelerationRate = UIScrollViewDecelerationRateNormal;
@@ -337,10 +690,13 @@
}
}
+ [self handleScrollSnapAttributes:attributes];
+
if ([self isViewLoaded]) {
- if (_pagingEnabled && _pageSize > 0) {
+ if ((_pagingEnabled && _pageSize > 0) || _snapData.useSnap) {
((UIScrollView *)self.view).pagingEnabled = NO; // turn off system default paging
((UIScrollView *)self.view).decelerationRate = UIScrollViewDecelerationRateFast;
+ [_snapData bindingScrollView:(UIScrollView *)self.view];
}
else {
((UIScrollView *)self.view).decelerationRate = UIScrollViewDecelerationRateNormal;
@@ -859,6 +1215,9 @@
if (!_isScrolling) {
[self dispatchScrollEndEvent:scrollView];
_scrollEndPoint = scrollView.contentOffset;
+ if (_snapData.useSnap && _snapData.snapping == true) {
+ _snapData.snapping = false;
+ }
id<WXPageEventNotifyEventProtocol> eventNotify = [WXSDKEngine handlerForProtocol:@protocol(WXPageEventNotifyEventProtocol)];
if ([eventNotify respondsToSelector:@selector(notifyScrollEvent:from:to:)]) {
[eventNotify notifyScrollEvent:self.weexInstance.instanceId from:_scrollStartPoint to:_scrollEndPoint];
@@ -873,8 +1232,49 @@
}
}
+- (CGPoint)calculateSnapPosition:(UIScrollView *)scrollView withVelocity:(CGPoint)velocity startPosition:(CGPoint)startPosition targetPosition:(CGPoint)targetPosition{
+ return CGPointMake(-1, -1);
+}
+
+- (void)setContentOffset:(CGPoint)contentOffset duration:(NSTimeInterval)duration timingFunction:(WXScrollAnimateFunction)timingFunction completion:(void(^)(void))completion {
+ if (!_scrollAnimator) {
+ _scrollAnimator = [[WXScrollAnimator alloc] initWithScrollView:(UIScrollView *)self.view timingFunction:timingFunction];
+ }
+ __weak typeof(self) weakSelf = self;
+ _scrollAnimator.completion = ^{
+ __strong typeof(self) strongSelf = weakSelf;
+ dispatch_async(dispatch_get_main_queue(), ^{
+ strongSelf.scrollAnimator = nil;
+ });
+ if (completion) {
+ completion();
+ }
+ };
+ [_scrollAnimator setContentOffset:contentOffset duration:duration];
+}
+
- (void)scrollViewWillEndDragging:(UIScrollView *)scrollView withVelocity:(CGPoint)velocity targetContentOffset:(inout CGPoint *)targetContentOffset
{
+ if (_snapData.useSnap) {
+ CGPoint offset = [self calculateSnapPosition:scrollView withVelocity:velocity startPosition:_scrollStartPoint targetPosition:CGPointMake(targetContentOffset->x, targetContentOffset->y)];
+ // offset beyond bounary use default animation, make sure bounce effect
+ if (offset.x + scrollView.frame.size.width > scrollView.contentSize.width ||
+ offset.y + scrollView.frame.size.height > scrollView.contentSize.height ||
+ (self.scrollDirection == WXScrollDirectionVertical && offset.y <= 0) ||
+ (self.scrollDirection == WXScrollDirectionHorizontal && offset.x <= 0)) {
+ targetContentOffset->x = offset.x;
+ targetContentOffset->y = offset.y;
+ }
+ else if (self.snapData.timingFunction != WXScrollAnimateNone) {
+ [self setContentOffset:offset duration:self.snapData.scrollAnimateDuration timingFunction:self.snapData.timingFunction completion:^{
+ WXLogInfo(@"snap scroll animate over");
+ }];
+ } else {
+ targetContentOffset->x = offset.x;
+ targetContentOffset->y = offset.y;
+ }
+ self.snapData.targetPosition = offset;
+ }
// Page stop effect
if (_pagingEnabled && _pageSize > 0) {
if (_scrollDirection == WXScrollDirectionVertical) {
@@ -1037,6 +1437,9 @@
_scrollEventListener(self, @"scrollend", @{@"contentSize":contentSizeData, @"contentOffset":contentOffsetData});
}
}
+ if (_snapData.useSnap) {
+ [self fireEvent:@"snapend" params:@{@"section" : @(self.snapData.targetIndexPath.section), @"row" : @(self.snapData.targetIndexPath.row)} domChanges:nil];
+ }
}
- (void)scrollToTarget:(WXScrollToTarget *)target scrollRect:(CGRect)rect
diff --git a/ios/sdk/WeexSDK/Sources/Component/WXTextComponent.mm b/ios/sdk/WeexSDK/Sources/Component/WXTextComponent.mm
index 27538dc..ac9f5a4 100644
--- a/ios/sdk/WeexSDK/Sources/Component/WXTextComponent.mm
+++ b/ios/sdk/WeexSDK/Sources/Component/WXTextComponent.mm
@@ -947,7 +947,7 @@
attrs = attrs ? attrs.mutableCopy : [NSMutableDictionary new];
CTFontRef font = (__bridge CTFontRef)(attrs[(id)kCTFontAttributeName]);
CGFloat fontSize = font ? CTFontGetSize(font):32 * self.weexInstance.pixelScaleFactor;
- UIFont * uiFont = [UIFont systemFontOfSize:fontSize];
+ UIFont *uiFont = [WXUtility fontWithSize:fontSize textWeight:_fontWeight textStyle:WXTextStyleNormal fontFamily:self.fontFamily scaleFactor:self.weexInstance.pixelScaleFactor useCoreText:[self useCoreText]];
if (uiFont) {
font = CTFontCreateWithFontDescriptor((__bridge CTFontDescriptorRef)uiFont.fontDescriptor, uiFont.pointSize, NULL);
}
@@ -1079,6 +1079,8 @@
CGFloat ascent = 0;
CGFloat descent = 0;
CGFloat leading = 0;
+ CGPoint lineOrigins[lineCount];
+ CTFrameGetLineOrigins(frameRef, CFRangeMake(0, 0), lineOrigins);
// height = ascent + descent + lineCount*leading
// ignore linespaing
@@ -1091,7 +1093,6 @@
totalHeight += ascent + descent;
actualLineCount ++;
}
-
totalHeight = totalHeight + actualLineCount * leading;
CFRelease(frameRef);
@@ -1102,6 +1103,18 @@
}
return CGSizeMake(aWidth, suggestSize.height);
}
+ if (WX_SYS_VERSION_GREATER_THAN_OR_EQUAL_TO(@"14.0")) {
+ if (lineCount <= 1) {
+ return CGSizeMake(aWidth, totalHeight);
+ }
+ if (_lines && lineCount > _lines) {
+ actualLineCount = _lines;
+ } else {
+ actualLineCount = lineCount;
+ }
+ CGFloat actualLineHeight = lineOrigins[0].y - lineOrigins[1].y;
+ return CGSizeMake(aWidth, actualLineCount * actualLineHeight);
+ }
return CGSizeMake(aWidth, totalHeight);
}
diff --git a/ios/sdk/WeexSDK/Sources/Engine/WXSDKError.h b/ios/sdk/WeexSDK/Sources/Engine/WXSDKError.h
index 0800003..fd4813e 100644
--- a/ios/sdk/WeexSDK/Sources/Engine/WXSDKError.h
+++ b/ios/sdk/WeexSDK/Sources/Engine/WXSDKError.h
@@ -63,7 +63,7 @@
WX_ERR_JS_EXECUTE = -2013,
WX_ERR_JSBRIDGE_END = -2099,
- WX_ERR_RENDER_START = -2100,
+ WX_ERR_RENDER_START = -2120,
WX_ERR_RENDER_CREATEBODY = -2100,
WX_ERR_RENDER_UPDATTR = -2101,
WX_ERR_RENDER_UPDSTYLE = -2102,
diff --git a/ios/sdk/WeexSDK/Sources/Events/WXComponent+Events.m b/ios/sdk/WeexSDK/Sources/Events/WXComponent+Events.m
index d7b594c..3177a7e 100644
--- a/ios/sdk/WeexSDK/Sources/Events/WXComponent+Events.m
+++ b/ios/sdk/WeexSDK/Sources/Events/WXComponent+Events.m
@@ -33,6 +33,8 @@
#import <UIKit/UIGestureRecognizerSubclass.h>
#import "WXComponent+PseudoClassManagement.h"
#import "WXCoreBridge.h"
+#import "WXSDKEngine.h"
+#import "WXConfigCenterProtocol.h"
#pragma clang diagnostic ignored "-Wobjc-protocol-method-implementation"
@@ -575,9 +577,16 @@
- (void)addPanGesture
{
if (!_panGesture) {
+ _enableScreenEdgePanGesture = YES;
_panGesture = [[UIPanGestureRecognizer alloc] initWithTarget:self action:@selector(onPan:)];
+
_panGesture.delegate = self;
[self.view addGestureRecognizer:_panGesture];
+
+ id configCenter = [WXSDKEngine handlerForProtocol:@protocol(WXConfigCenterProtocol)];
+ if ([configCenter respondsToSelector:@selector(configForKey:defaultValue:isDefault:)]) {
+ _enableScreenEdgePanGesture = [[configCenter configForKey:@"iOS_weex_ext_config.enableScreenEdgePanGesture" defaultValue:@(YES) isDefault:NULL] boolValue];
+ }
}
}
@@ -840,6 +849,12 @@
if ([gestureRecognizer isKindOfClass:[WXTouchGestureRecognizer class]]) {
return YES;
}
+
+ if (_enableScreenEdgePanGesture && gestureRecognizer == _panGesture && [otherGestureRecognizer isKindOfClass:[UIPanGestureRecognizer class]] && ![otherGestureRecognizer isKindOfClass:NSClassFromString(panGestureRecog)]) {
+ [gestureRecognizer requireGestureRecognizerToFail:otherGestureRecognizer];
+ return YES;
+ }
+
// swipe and scroll
if ([gestureRecognizer isKindOfClass:[UISwipeGestureRecognizer class]] && [otherGestureRecognizer isKindOfClass:NSClassFromString(panGestureRecog)]) {
return YES;
diff --git a/ios/sdk/WeexSDK/Sources/Handler/WXUnicornEventListenerHandler.h b/ios/sdk/WeexSDK/Sources/Handler/WXUnicornEventListenerHandler.h
new file mode 100644
index 0000000..a3fe4c3
--- /dev/null
+++ b/ios/sdk/WeexSDK/Sources/Handler/WXUnicornEventListenerHandler.h
@@ -0,0 +1,30 @@
+/*
+ * 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 <Foundation/Foundation.h>
+
+NS_ASSUME_NONNULL_BEGIN
+
+@interface WXUnicornEventListenerHandler : NSObject
+
++ (void)fireEvent:(NSDictionary *)args;
+
+@end
+
+NS_ASSUME_NONNULL_END
+
diff --git a/ios/sdk/WeexSDK/Sources/Handler/WXUnicornEventListenerHandler.m b/ios/sdk/WeexSDK/Sources/Handler/WXUnicornEventListenerHandler.m
new file mode 100644
index 0000000..23d461b
--- /dev/null
+++ b/ios/sdk/WeexSDK/Sources/Handler/WXUnicornEventListenerHandler.m
@@ -0,0 +1,36 @@
+/*
+ * 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 "WXUnicornEventListenerHandler.h"
+
+#import "WXSDKManager.h"
+
+@implementation WXUnicornEventListenerHandler
+
++ (void)fireEvent:(NSDictionary *)args {
+ NSMutableDictionary* eventParams = [args[@"params"] mutableCopy];
+ NSTimeInterval timeSp = [[NSDate date] timeIntervalSince1970] * 1000;
+ [eventParams setObject:@(timeSp) forKey:@"timestamp"];
+ NSDictionary* domChanges = [args[@"domChanges"] copy];
+ [[WXSDKManager bridgeMgr] fireEvent:args[@"pageId"] ref:args[@"ref"] type:args[@"type"] params:eventParams domChanges:domChanges?:@{}];
+}
+
+@end
+
+
diff --git a/ios/sdk/WeexSDK/Sources/Manager/WXBridgeManager.h b/ios/sdk/WeexSDK/Sources/Manager/WXBridgeManager.h
index e5fefd3..295fcd8 100644
--- a/ios/sdk/WeexSDK/Sources/Manager/WXBridgeManager.h
+++ b/ios/sdk/WeexSDK/Sources/Manager/WXBridgeManager.h
@@ -248,6 +248,8 @@
- (void)callJSMethod:(NSString *)method args:(NSArray *)args;
+- (void)callJSMethod:(NSString *)method args:(NSArray *)args completion:(void (^ _Nullable)(JSValue * _Nullable))completion;
+
- (void)executeJSTaskQueue;
- (void)checkJSThread;
diff --git a/ios/sdk/WeexSDK/Sources/Manager/WXBridgeManager.m b/ios/sdk/WeexSDK/Sources/Manager/WXBridgeManager.m
index 5c1f455..7b1da3d 100644
--- a/ios/sdk/WeexSDK/Sources/Manager/WXBridgeManager.m
+++ b/ios/sdk/WeexSDK/Sources/Manager/WXBridgeManager.m
@@ -769,8 +769,7 @@
});
}
-- (void)callJSMethod:(NSString *)method args:(NSArray *)args
-{
+- (void)callJSMethod:(NSString *)method args:(NSArray *)args completion:(void (^)(JSValue *))completion {
if (!method) return;
__weak typeof(self) weakSelf = self;
@@ -778,10 +777,14 @@
WXPerformBlockOnBridgeThreadForInstance(^(){
WXSDKInstance* sdkInstance = [WXSDKManager instanceForID:instanceId];
WXBridgeContext* context = sdkInstance && sdkInstance.useBackupJsThread ? weakSelf.backupBridgeCtx : weakSelf.bridgeCtx;
- [context callJSMethod:method args:args onContext:nil completion:nil];
+ [context callJSMethod:method args:args onContext:nil completion:completion];
}, instanceId);
}
+- (void)callJSMethod:(NSString *)method args:(NSArray *)args {
+ [self callJSMethod:method args:args completion:nil];
+}
+
#pragma mark JS Thread Check
- (void)checkJSThread {
if (!_timer) {
diff --git a/ios/sdk/WeexSDK/Sources/Manager/WXDatePickerManager.m b/ios/sdk/WeexSDK/Sources/Manager/WXDatePickerManager.m
index 9c67181..0636fa4 100644
--- a/ios/sdk/WeexSDK/Sources/Manager/WXDatePickerManager.m
+++ b/ios/sdk/WeexSDK/Sources/Manager/WXDatePickerManager.m
@@ -22,6 +22,7 @@
#import <UIKit/UIKit.h>
#import "WXConvert.h"
#import "WXUtility.h"
+#import "WXLegacyAdapter.h"
#define WXPickerHeight 266
@@ -61,13 +62,12 @@
{
datePicker = [[UIDatePicker alloc]init];
}
-
- datePicker.datePickerMode=UIDatePickerModeDate;
-#ifdef __IPHONE_13_4
+ #if (defined(__IPHONE_OS_VERSION_MAX_ALLOWED) && __IPHONE_OS_VERSION_MAX_ALLOWED >= 130400)
if (@available(iOS 13.4, *)) {
datePicker.preferredDatePickerStyle = UIDatePickerStyleWheels;
}
-#endif
+ #endif
+ datePicker.datePickerMode=UIDatePickerModeDate;
CGRect pickerFrame = CGRectMake(0, 44, [UIScreen mainScreen].bounds.size.width, WXPickerHeight-44);
datePicker.backgroundColor = [UIColor whiteColor];
datePicker.frame = pickerFrame;
diff --git a/ios/sdk/WeexSDK/Sources/Model/WXSDKInstance.m b/ios/sdk/WeexSDK/Sources/Model/WXSDKInstance.m
index 2fd7bb7..3e5ad21 100644
--- a/ios/sdk/WeexSDK/Sources/Model/WXSDKInstance.m
+++ b/ios/sdk/WeexSDK/Sources/Model/WXSDKInstance.m
@@ -91,6 +91,9 @@
- (void)dealloc
{
+ if (self.unicornRender) {
+ [self.unicornRender shutdown];
+ }
[_moduleEventObservers removeAllObjects];
[self removeObservers];
}
@@ -364,6 +367,16 @@
self.needValidate = [[WXHandlerFactory handlerForProtocol:@protocol(WXValidateProtocol)] needValidate:url];
WXResourceRequest *request = [WXResourceRequest requestWithURL:url resourceType:WXResourceTypeMainBundle referrer:@"" cachePolicy:NSURLRequestUseProtocolCachePolicy];
[self _renderWithRequest:request options:options data:data];
+ if ([options[@"USE_UNICORN"] boolValue]) {
+ __weak typeof(self) weakSelf = self;
+ WXPerformBlockOnMainThread(^{
+ __strong typeof(weakSelf) strongSelf = weakSelf;
+ if (strongSelf == nil) {
+ return;
+ }
+ [strongSelf initUnicornRender];
+ });
+ }
if (self.renderPlugin.isSupportExecScript) {
NSMutableDictionary *newOptions = [NSMutableDictionary dictionaryWithDictionary:options];
@@ -389,7 +402,21 @@
[[WXSDKManager bridgeMgr] executeJSTaskQueue];
}
+ [self _checkPageName];
+ [self.apmInstance startRecord:self.instanceId];
+ self.apmInstance.isStartRender = YES;
+
self.needValidate = [[WXHandlerFactory handlerForProtocol:@protocol(WXValidateProtocol)] needValidate:self.scriptURL];
+ if ([options[@"USE_UNICORN"] boolValue]) {
+ __weak typeof(self) weakSelf = self;
+ WXPerformBlockOnMainThread(^{
+ __strong typeof(weakSelf) strongSelf = weakSelf;
+ if (strongSelf == nil) {
+ return;
+ }
+ [strongSelf initUnicornRender];
+ });
+ }
if ([source isKindOfClass:[NSString class]]) {
WXLogDebug(@"Render source: %@, data:%@", self, [WXUtility JSONString:data]);
@@ -523,21 +550,19 @@
WXLogError(@"Fail to find instance!");
return;
}
-
+
if (_isRendered) {
[WXExceptionUtils commitCriticalExceptionRT:self.instanceId errCode:[NSString stringWithFormat:@"%d", WX_ERR_RENDER_TWICE] function:@"_renderWithMainBundleString:" exception:[NSString stringWithFormat:@"instance is rendered twice"] extParams:nil];
return;
}
-
- //some case , with out render (url)
- [self _checkPageName];
- [self.apmInstance startRecord:self.instanceId];
- self.apmInstance.isStartRender = YES;
[_apmInstance setProperty:KEY_PAGE_PROPERTIES_UIKIT_TYPE withValue:_renderType?: WEEX_RENDER_TYPE_PLATFORM];
if (self.renderPlugin) {
[self.apmInstance setProperty:KEY_PAGE_PROPERTIES_RENDER_TYPE withValue:@"eagle"];
}
+ if (_options[@"USE_UNICORN"]) {
+ [self.apmInstance setProperty:KEY_PAGE_PROPERTIES_RENDER_TYPE withValue:@"weex2"];
+ }
self.performance.renderTimeOrigin = CACurrentMediaTime()*1000;
self.performance.renderUnixTimeOrigin = [WXUtility getUnixFixTimeMillis];
@@ -581,10 +606,15 @@
if ([WXDebugTool getReplacedBundleJS]) {
mainBundleString = [WXDebugTool getReplacedBundleJS];
}
-
+
WXPerformBlockOnMainThread(^{
if (self.isCustomRenderType) {
self->_rootView = [WXCustomPageBridge createPageRootView:self.instanceId pageType:self.renderType frame:self.frame];
+ } else if ([self->_options[@"USE_UNICORN"] boolValue]) {
+ [self initUnicornRender];
+ self->_rootView = [[WXRootView alloc] initWithFrame:self.frame];
+ [self->_rootView addSubview:self.unicornRender.rootView];
+ ((WXRootView*)(self->_rootView)).instance = self;
}
else {
self->_rootView = [[WXRootView alloc] initWithFrame:self.frame];
@@ -862,14 +892,7 @@
[WXPrerenderManager removePrerenderTaskforUrl:[self.scriptURL absoluteString]];
[WXPrerenderManager destroyTask:self.instanceId];
- if (_useReactor) {
- id<WXReactorProtocol> reactorHandler = [WXHandlerFactory handlerForProtocol:NSProtocolFromString(@"WXReactorProtocol")];
- if (reactorHandler) {
- [reactorHandler unregisterJSContext:self.instanceId];
- } else {
- WXLogError(@"There is no reactor handler");
- }
- } else if (!self.renderPlugin) {
+ if (!self.renderPlugin && !_useReactor) {
[[WXSDKManager bridgeMgr] destroyInstance:self.instanceId];
}
@@ -900,6 +923,14 @@
// Reading config from orange for Release instance in Main Thread or not, for Bug #15172691 +{
dispatch_async(dispatch_get_main_queue(), ^{
+ if (self.useReactor) {
+ id<WXReactorProtocol> reactorHandler = [WXHandlerFactory handlerForProtocol:NSProtocolFromString(@"WXReactorProtocol")];
+ if (reactorHandler) {
+ [reactorHandler unregisterJSContext:self.instanceId];
+ } else {
+ WXLogError(@"There is no reactor handler");
+ }
+ }
[WXSDKManager removeInstanceforID:instanceId];
WXLogInfo(@"Finally remove instance: %@", instanceId);
});
@@ -1142,6 +1173,22 @@
}
}
+- (void)initUnicornRender {
+ if (_unicornRender) {
+ return;
+ }
+ Class UnicornRenderClass = NSClassFromString(@"UnicornRender");
+ if (UnicornRenderClass) {
+ [self.apmInstance onStage:KEY_PAGE_UNICORN_ENGINE_INIT_START];
+ _unicornRender = (id<WXUnicornRenderProtocol>)[[UnicornRenderClass alloc] initWithInstanceId:self.instanceId];
+ _unicornRender.frame = self.frame;
+ [_unicornRender startEngine:self->_viewController];
+ [self.apmInstance onStage:KEY_PAGE_UNICORN_ENGINE_INIT_END];
+ } else {
+ WXLogError(@"There is no UnicornRender");
+ }
+}
+
- (void)applicationWillResignActive:(NSNotification*)notification
{
[self fireGlobalEvent:WX_APPLICATION_WILL_RESIGN_ACTIVE params:nil];
diff --git a/ios/sdk/WeexSDK/Sources/Model/WXSDKInstance_private.h b/ios/sdk/WeexSDK/Sources/Model/WXSDKInstance_private.h
index 6ea7e4d..51d79ae 100644
--- a/ios/sdk/WeexSDK/Sources/Model/WXSDKInstance_private.h
+++ b/ios/sdk/WeexSDK/Sources/Model/WXSDKInstance_private.h
@@ -21,6 +21,7 @@
#import "WXSDKInstance.h"
#import "WXModuleMethod.h"
#import "WXThreadSafeMutableDictionary.h"
+#import "WXUnicornRenderProtocol.h"
#import <JavaScriptCore/JavaScriptCore.h>
#import <WeexSDK/WXEaglePlugin.h>
@@ -38,6 +39,8 @@
@property (nonatomic, assign) BOOL useReactor;
+@property (nonatomic, strong) id<WXUnicornRenderProtocol> unicornRender;
+
// add monitor information
@property (nonatomic, strong) NSString *callCreateInstanceContext;
@property (nonatomic, strong) NSString *createInstanceContextResult;
diff --git a/ios/sdk/WeexSDK/Sources/Module/WXDomModule.m b/ios/sdk/WeexSDK/Sources/Module/WXDomModule.m
index 4b3607b..3a892ad 100644
--- a/ios/sdk/WeexSDK/Sources/Module/WXDomModule.m
+++ b/ios/sdk/WeexSDK/Sources/Module/WXDomModule.m
@@ -31,6 +31,8 @@
#import "WXRecycleListComponent.h"
#import "WXCoreBridge.h"
#import <objc/message.h>
+#import "WXSDKInstance_performance.h"
+#import "WXMonitor.h"
@interface WXDomModule ()
@@ -155,7 +157,27 @@
- (void)createFinish
{
NSString* instanceId = self.weexInstance.instanceId;
- if ([WXCustomPageBridge isCustomPage:instanceId]) {
+ if (self.weexInstance.unicornRender) {
+ __weak typeof(self) weakSelf = self;
+ WXPerformBlockOnMainThread(^{
+ __strong WXDomModule* strongSelf = weakSelf;
+ if (strongSelf == nil) {
+ return;
+ }
+
+ WX_MONITOR_INSTANCE_PERF_END(WXPTFirstScreenRender, strongSelf.weexInstance);
+ WX_MONITOR_INSTANCE_PERF_END(WXPTAllRender, strongSelf.weexInstance);
+ WX_MONITOR_SUCCESS(WXMTJSBridge);
+ WX_MONITOR_SUCCESS(WXMTNativeRender);
+ [strongSelf.weexInstance updatePerDicAfterCreateFinish];
+
+ UIView *rootView = strongSelf.weexInstance.rootView;
+ [strongSelf.weexInstance.performance onInstanceRenderSuccess:strongSelf.weexInstance];
+ if (strongSelf.weexInstance.renderFinish) {
+ strongSelf.weexInstance.renderFinish(rootView);
+ }
+ });
+ } else if ([WXCustomPageBridge isCustomPage:instanceId]) {
[[WXCustomPageBridge sharedInstance] callCreateFinish:instanceId];
}
else {
diff --git a/ios/sdk/WeexSDK/Sources/Module/WXModalUIModule.m b/ios/sdk/WeexSDK/Sources/Module/WXModalUIModule.m
index 6e6f353..4607e4a 100644
--- a/ios/sdk/WeexSDK/Sources/Module/WXModalUIModule.m
+++ b/ios/sdk/WeexSDK/Sources/Module/WXModalUIModule.m
@@ -65,6 +65,8 @@
@interface WXModalUIModule () <UIAlertViewDelegate>
+@property (nonatomic, assign) CGFloat maxWidth;
+
@end
@implementation WXModalUIModule
@@ -107,9 +109,52 @@
duration = WXToastDefaultDuration;
}
- WXPerformBlockOnMainThread(^{
- [self toast:message duration:duration];
- });
+ _maxWidth = WXToastDefaultWidth;
+ if (param[@"maxWidth"]) {
+ double maxWidth = [param[@"maxWidth"] doubleValue];
+ if (maxWidth > 0) {
+ _maxWidth = maxWidth;
+ }
+ }
+
+ double animationTime = [param[@"animationTime"] doubleValue];
+ if (animationTime > 0) {
+ WXPerformBlockOnMainThread(^{
+ [self toast:message duration:duration animationTime:animationTime];
+ });
+ } else {
+ WXPerformBlockOnMainThread(^{
+ [self toast:message duration:duration];
+ });
+ }
+}
+
+- (void)toast:(NSString *)message duration:(double)duration animationTime:(double)animationTime{
+ WXAssertMainThread();
+ UIView *superView = self.weexInstance.rootView.window;
+ if (!superView) {
+ superView = self.weexInstance.rootView;
+ }
+ UIView *toastView = [self toastViewForMessage:message superView:superView];
+
+ UIView* toastingView = [WXToastManager sharedManager].toastingView;
+ if (toastingView) {
+ [toastingView removeFromSuperview];
+ [WXToastManager sharedManager].toastingView = nil;
+ }
+ if (!toastView || !superView) {
+ return;
+ }
+ [WXToastManager sharedManager].toastingView = toastView;
+ [superView addSubview:toastView];
+ [UIView animateWithDuration:animationTime delay:duration options:UIViewAnimationOptionCurveEaseInOut animations:^{
+ toastView.alpha = 0;
+ } completion:^(BOOL finished) {
+ [toastView removeFromSuperview];
+ if ([WXToastManager sharedManager].toastingView == toastView) {
+ [WXToastManager sharedManager].toastingView = nil;
+ }
+ }];
}
- (void)toast:(NSString *)message duration:(double)duration
@@ -132,7 +177,7 @@
- (UIView *)toastViewForMessage:(NSString *)message superView:(UIView *)superView
{
CGFloat padding = WXToastDefaultPadding;
- UILabel *messageLabel = [[UILabel alloc] initWithFrame:CGRectMake(padding/2, padding/2, WXToastDefaultWidth, WXToastDefaultHeight)];
+ UILabel *messageLabel = [[UILabel alloc] initWithFrame:CGRectMake(padding/2, padding/2, _maxWidth, WXToastDefaultHeight)];
messageLabel.numberOfLines = 0;
messageLabel.textAlignment = NSTextAlignmentCenter;
messageLabel.text = message;
diff --git a/ios/sdk/WeexSDK/Sources/Module/WXPickerModule.m b/ios/sdk/WeexSDK/Sources/Module/WXPickerModule.m
index c5b57a3..0c4da1c 100644
--- a/ios/sdk/WeexSDK/Sources/Module/WXPickerModule.m
+++ b/ios/sdk/WeexSDK/Sources/Module/WXPickerModule.m
@@ -24,6 +24,7 @@
#import <UIKit/UIPickerView.h>
#import <UIKit/UIDatePicker.h>
#import <UIKit/UIKit.h>
+#import "WXLegacyAdapter.h"
#define WXPickerHeight 266
#define WXPickerToolBarHeight 44
@@ -432,7 +433,7 @@
{
self.callback = callback;
self.datePicker = [[UIDatePicker alloc]init];
-#ifdef __IPHONE_13_4
+#if (defined(__IPHONE_OS_VERSION_MAX_ALLOWED) && __IPHONE_OS_VERSION_MAX_ALLOWED >= 130400)
if (@available(iOS 13.4, *)) {
self.datePicker.preferredDatePickerStyle = UIDatePickerStyleWheels;
}
diff --git a/ios/sdk/WeexSDK/Sources/Module/WXStreamModule.m b/ios/sdk/WeexSDK/Sources/Module/WXStreamModule.m
index 72106b9..7c6e3fd 100644
--- a/ios/sdk/WeexSDK/Sources/Module/WXStreamModule.m
+++ b/ios/sdk/WeexSDK/Sources/Module/WXStreamModule.m
@@ -27,6 +27,7 @@
#import "WXSDKEngine.h"
#import "WXSDKInstance_performance.h"
#import "WXMonitor.h"
+#import "WXConvert.h"
@implementation WXStreamModule
@@ -141,9 +142,17 @@
if (self.weexInstance && !weexInstance.isJSCreateFinish) {
self.weexInstance.performance.fsReqNetNum++;
}
-
- WXResourceRequest *request = [WXResourceRequest requestWithURL:[NSURL URLWithString:urlStr] resourceType:WXResourceTypeOthers referrer:nil cachePolicy:NSURLRequestUseProtocolCachePolicy];
-
+ BOOL shouldSetReferer = NO;
+ if ([options objectForKey:@"referer"] ) {
+ shouldSetReferer = [WXConvert BOOL:[options objectForKey:@"referer"]];
+ }
+ NSRange range = [weexInstance.scriptURL.absoluteString rangeOfString:@"?"];
+ NSString *referer = nil;
+ if (range.length > 0 && shouldSetReferer) {
+ referer = [weexInstance.scriptURL.absoluteString substringToIndex:range.location];
+ }
+ WXResourceRequest *request = [WXResourceRequest requestWithURL:[NSURL URLWithString:urlStr] resourceType:WXResourceTypeOthers referrer:referer cachePolicy:NSURLRequestUseProtocolCachePolicy];
+
// parse http method
NSString *method = [options objectForKey:@"method"];
if ([WXUtility isBlankString:method]) {
diff --git a/ios/sdk/WeexSDK/Sources/Performance/WXApmForInstance.h b/ios/sdk/WeexSDK/Sources/Performance/WXApmForInstance.h
index 7ed92fe..3a19547 100644
--- a/ios/sdk/WeexSDK/Sources/Performance/WXApmForInstance.h
+++ b/ios/sdk/WeexSDK/Sources/Performance/WXApmForInstance.h
@@ -46,6 +46,7 @@
///************** stages *****************/
extern NSString* const KEY_PAGE_STAGES_START;
+extern NSString* const KEY_PAGE_STAGES_CONTAINER_READY;
extern NSString* const KEY_PAGE_STAGES_DOWN_BUNDLE_START;
extern NSString* const KEY_PAGE_STAGES_DOWN_BUNDLE_END;
extern NSString* const KEY_PAGE_STAGES_DOWN_JS_START;
@@ -63,6 +64,10 @@
extern NSString* const KEY_PAGE_STAGES_NEW_FSRENDER;
extern NSString* const KEY_PAGE_STAGES_INTERACTION;
extern NSString* const KEY_PAGE_STAGES_DESTROY;
+extern NSString* const KEY_PAGE_STAGES_CREATE_INSTANCE_START;
+extern NSString* const KEY_PAGE_STAGES_CREATE_INSTANCE_END;
+extern NSString* const KEY_PAGE_UNICORN_ENGINE_INIT_START;
+extern NSString* const KEY_PAGE_UNICORN_ENGINE_INIT_END;
///************** stats *****************/
extern NSString* const KEY_PAGE_STATS_BUNDLE_SIZE;
diff --git a/ios/sdk/WeexSDK/Sources/Performance/WXApmForInstance.m b/ios/sdk/WeexSDK/Sources/Performance/WXApmForInstance.m
index 712afd0..a529a2f 100644
--- a/ios/sdk/WeexSDK/Sources/Performance/WXApmForInstance.m
+++ b/ios/sdk/WeexSDK/Sources/Performance/WXApmForInstance.m
@@ -30,6 +30,7 @@
#import "WXExceptionUtils.h"
#import "WXSDKInstance_performance.h"
#import "WXAnalyzerCenter+Transfer.h"
+#import "WXSDKInstance_private.h"
#pragma mark - const static string
@@ -57,6 +58,7 @@
///************** stages *****************/
NSString* const KEY_PAGE_STAGES_START = @"wxRecordStart";
+NSString* const KEY_PAGE_STAGES_CONTAINER_READY = @"wxContainerReady";
NSString* const KEY_PAGE_STAGES_DOWN_BUNDLE_START = @"wxStartDownLoadBundle";
NSString* const KEY_PAGE_STAGES_DOWN_BUNDLE_END = @"wxEndDownLoadBundle";
NSString* const KEY_PAGE_STAGES_DOWN_JS_START = @"wxStartDownLoadJS";
@@ -73,7 +75,12 @@
NSString* const KEY_PAGE_STAGES_FSRENDER = @"wxFsRender";
NSString* const KEY_PAGE_STAGES_NEW_FSRENDER = @"wxNewFsRender";
NSString* const KEY_PAGE_STAGES_INTERACTION = @"wxInteraction";
+NSString* const KEY_PAGE_STAGES_INTERACTION_TM = @"wxInteractionTimeStamp";
NSString* const KEY_PAGE_STAGES_DESTROY = @"wxDestroy";
+NSString* const KEY_PAGE_STAGES_CREATE_INSTANCE_START = @"wxCreateInstanceStart";
+NSString* const KEY_PAGE_STAGES_CREATE_INSTANCE_END = @"wxCreateInstanceEnd";
+NSString* const KEY_PAGE_UNICORN_ENGINE_INIT_START = @"wxUnicornEngineInitStart";
+NSString* const KEY_PAGE_UNICORN_ENGINE_INIT_END = @"wxUnicornEngineInitEnd";
///************** stats *****************/
NSString* const KEY_PAGE_STATS_BUNDLE_SIZE = @"wxBundleSize";
@@ -346,6 +353,7 @@
if (nil != _apmProtocolInstance) {
[self.apmProtocolInstance onStart:instanceId topic:WEEX_PAGE_TOPIC];
}
+ [self onStage:KEY_PAGE_STAGES_CONTAINER_READY];
[self onStage:KEY_PAGE_STAGES_START];
WXSDKInstance* instance = [WXSDKManager instanceForID:instanceId];
if (nil == instance) {
@@ -379,11 +387,23 @@
if (_isEnd) {
return;
}
- _isEnd = YES;
+ WXSDKInstance* instance = [WXSDKManager instanceForID:self.instanceId];
[self onStage:KEY_PAGE_STAGES_DESTROY];
+ if (instance.unicornRender) {
+ [self onStageWithTime:KEY_PAGE_STAGES_INTERACTION_TM time:[instance.unicornRender getFirstScreenTimeStamp]];
+ [self onStageWithTime:KEY_PAGE_STAGES_INTERACTION time:[instance.unicornRender getFirstScreenTimeInterval] + [WXUtility getIntervalTime]];
+
+ NSString* timeLine = [instance.unicornRender getEngineTimeline];
+ NSDictionary* timeLineDic = [WXUtility objectFromJSON:timeLine];
+ for (NSString* key in timeLineDic) {
+ long time = [[timeLineDic objectForKey:key] longLongValue] + [WXUtility getIntervalTime];
+ [self onStageWithTime:[@"wxUni" stringByAppendingString:key] time:time];
+ }
+ }
if (nil != _apmProtocolInstance) {
[self.apmProtocolInstance onEnd];
}
+ _isEnd = YES;
WXPerformBlockOnComponentThread(^{
WXLogInfo(@"APM data of instance: %@, %@", self.instanceId, self.recordStageMap);
diff --git a/ios/sdk/WeexSDK/Sources/Protocol/WXUnicornRenderProtocol.h b/ios/sdk/WeexSDK/Sources/Protocol/WXUnicornRenderProtocol.h
new file mode 100644
index 0000000..f5c97ed
--- /dev/null
+++ b/ios/sdk/WeexSDK/Sources/Protocol/WXUnicornRenderProtocol.h
@@ -0,0 +1,63 @@
+/*
+ * 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 <Foundation/Foundation.h>
+
+
+#ifdef __cplusplus
+#include <JavaScriptCore/JavaScriptCore.h>
+#include <string>
+#endif
+
+NS_ASSUME_NONNULL_BEGIN
+
+@protocol WXUnicornRenderProtocol <NSObject>
+
+@property (nonatomic, assign) CGRect frame;
+
+@property (nonatomic, strong) UIView *rootView;
+
+@property(nonatomic, copy) NSString * instanceId;
+
+- (instancetype)initWithInstanceId:(NSString *)instanceId;
+
+- (void)startEngine:(UIViewController*)parentViewController;
+
+- (void)shutdown;
+
+- (uint64_t)getFirstScreenTimeStamp;
+- (uint64_t)getFirstScreenTimeInterval;
+
+- (NSString*)getEngineTimeline;
+
+#ifdef __cplusplus
+typedef void(*UnicornRenderFunc)(const std::string& instance_id,
+ const std::string& module,
+ const std::string& method,
+ JSContextRef,
+ _Nullable JSValueRef* _Nullable,
+ int argc);
++ (UnicornRenderFunc)getRenderFunc;
+#endif
+
++ (void)installUnicornExternalAdapterImageProvider:(id)provider;
+
+@end
+
+NS_ASSUME_NONNULL_END
diff --git a/ios/sdk/WeexSDK/Sources/Utility/WXConvert.m b/ios/sdk/WeexSDK/Sources/Utility/WXConvert.m
index 85ab360..b749144 100644
--- a/ios/sdk/WeexSDK/Sources/Utility/WXConvert.m
+++ b/ios/sdk/WeexSDK/Sources/Utility/WXConvert.m
@@ -389,13 +389,22 @@
unichar t = [rgba characterAtIndex:3];
rgba = [NSString stringWithFormat:@"#%C%C%C%C%C%C", f, f, s, s, t, t];
}
-
- // 3. #rrggbb
- uint32_t colorValue = 0;
- sscanf(rgba.UTF8String, "#%x", &colorValue);
- red = ((colorValue & 0xFF0000) >> 16) / 255.0;
- green = ((colorValue & 0x00FF00) >> 8) / 255.0;
- blue = (colorValue & 0x0000FF) / 255.0;
+ // 2. #aarrggbb
+ if ([rgba length] == 9) {
+ uint32_t colorValue = 0;
+ sscanf(rgba.UTF8String, "#%x", &colorValue);
+ alpha = ((colorValue & 0xFF000000) >> 24) / 255.0;
+ red = ((colorValue & 0x00FF0000) >> 16) / 255.0;
+ green = ((colorValue & 0x0000FF00) >> 8) / 255.0;
+ blue = (colorValue & 0x000000FF) / 255.0;
+ } else {
+ // 3. #rrggbb
+ uint32_t colorValue = 0;
+ sscanf(rgba.UTF8String, "#%x", &colorValue);
+ red = ((colorValue & 0xFF0000) >> 16) / 255.0;
+ green = ((colorValue & 0x00FF00) >> 8) / 255.0;
+ blue = (colorValue & 0x0000FF) / 255.0;
+ }
} else if ([rgba hasPrefix:@"rgb("]) {
// 4. rgb(r,g,b)
int r,g,b;
diff --git a/ios/sdk/WeexSDK/Sources/Utility/WXLegacyAdapter.h b/ios/sdk/WeexSDK/Sources/Utility/WXLegacyAdapter.h
new file mode 100644
index 0000000..6c50ff0
--- /dev/null
+++ b/ios/sdk/WeexSDK/Sources/Utility/WXLegacyAdapter.h
@@ -0,0 +1,33 @@
+/*
+ * 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.
+ */
+
+#ifndef WXLegacyAdapter_h
+#define WXLegacyAdapter_h
+
+API_AVAILABLE(ios(13.4))
+@interface UIDatePicker (LegacyXcode)
+
+#if (defined(__IPHONE_OS_VERSION_MAX_ALLOWED) && __IPHONE_OS_VERSION_MAX_ALLOWED >= 130400)
+@property (nonatomic, readwrite, assign) UIDatePickerStyle preferredDatePickerStyle;
+#endif
+
+@end
+
+
+#endif /* WXLegacyAdapter_h */
diff --git a/ios/sdk/WeexSDK/Sources/Utility/WXUtility.h b/ios/sdk/WeexSDK/Sources/Utility/WXUtility.h
index 0112322..66d6ad8 100644
--- a/ios/sdk/WeexSDK/Sources/Utility/WXUtility.h
+++ b/ios/sdk/WeexSDK/Sources/Utility/WXUtility.h
@@ -540,6 +540,8 @@
+ (long) getUnixFixTimeMillis;
++ (long)getIntervalTime;
+
+ (NSArray<NSString *> *_Nullable)extractPropertyNamesOfJSValueObject:(JSValue *_Nullable)jsvalue;
@end
diff --git a/ios/sdk/WeexSDK/Sources/Utility/WXUtility.m b/ios/sdk/WeexSDK/Sources/Utility/WXUtility.m
index ebe1a8f..6caf13e 100644
--- a/ios/sdk/WeexSDK/Sources/Utility/WXUtility.m
+++ b/ios/sdk/WeexSDK/Sources/Utility/WXUtility.m
@@ -46,6 +46,7 @@
static BOOL enableRTLLayoutDirection = YES;
static BOOL isDarkSchemeSupportEnabled = YES;
static BOOL enableAdaptiveLayout = NO;
+static long intervalTime = 0;
void WXPerformBlockOnMainThread(void (^ _Nonnull block)(void))
{
@@ -1115,10 +1116,15 @@
dispatch_once(&unixTimeToken, ^{
sInterval = [[NSDate date] timeIntervalSince1970] * 1000 - CACurrentMediaTime()*1000;
+ intervalTime = sInterval;
});
return sInterval+CACurrentMediaTime()*1000;
}
++ (long)getIntervalTime {
+ return intervalTime;
+}
+
+ (NSArray<NSString *> *)extractPropertyNamesOfJSValueObject:(JSValue *)jsvalue
{
if (!jsvalue) {
diff --git a/ios/sdk/WeexSDK/Sources/WeexSDK.h b/ios/sdk/WeexSDK/Sources/WeexSDK.h
index 372078a..e992aeb 100644
--- a/ios/sdk/WeexSDK/Sources/WeexSDK.h
+++ b/ios/sdk/WeexSDK/Sources/WeexSDK.h
@@ -33,6 +33,8 @@
#import <WeexSDK/WXView.h>
#import <WeexSDK/WXValidateProtocol.h>
#import <WeexSDK/WXUtility.h>
+#import <WeexSDK/WXUnicornRenderProtocol.h>
+#import <WeexSDK/WXUnicornEventListenerHandler.h>
#import <WeexSDK/WXURLRewriteProtocol.h>
#import <WeexSDK/WXType.h>
#import <WeexSDK/WXStreamModule.h>
diff --git a/weex_core/Source/core/render/page/reactor_page.cpp b/weex_core/Source/core/render/page/reactor_page.cpp
index 081e9b33..7e62e80 100644
--- a/weex_core/Source/core/render/page/reactor_page.cpp
+++ b/weex_core/Source/core/render/page/reactor_page.cpp
@@ -190,6 +190,11 @@
options_length);
}
+void ReactorPage::ReportJSException(const std::string& message){
+ WeexCoreManager::Instance()->getPlatformBridge()
+ ->platform_side()->ReportException(page_id_.c_str(), "ReactorException", message.c_str());
+}
+
RenderObject* ReactorPage::CreateRenderObject(const std::string& ref,
const std::string& type,
unsigned index,
@@ -224,5 +229,7 @@
return render_object;
}
+
+
} // namespace WeexCore
diff --git a/weex_core/Source/core/render/page/reactor_page.h b/weex_core/Source/core/render/page/reactor_page.h
index dd32e8a..f6d10b3 100644
--- a/weex_core/Source/core/render/page/reactor_page.h
+++ b/weex_core/Source/core/render/page/reactor_page.h
@@ -73,6 +73,7 @@
const std::string& options,
int options_length);
+ void ReportJSException(const std::string& message);
private:
RenderObject* CreateRenderObject(const std::string& ref, const std::string& type, unsigned index, const std::map<std::string, std::string>& styles, const std::map<std::string, std::string>& attrs, const std::vector<std::string>& events, bool reserve_styles, WeexCore::RenderObject* parent);