diff --git a/.github/CODEOWNERS b/.github/CODEOWNERS index 5ed2a311247..ba42c25f6fe 100644 --- a/.github/CODEOWNERS +++ b/.github/CODEOWNERS @@ -41,7 +41,7 @@ CMakeLists.txt @ilikethese @etkmao /framework/ios/ @wwwcg @ruifanyuan # framework: voltron -/framework/voltron/ @lvfen @skindhu +/framework/voltron/ @lvfen @henryjin0511 # dom: others /dom/ @etkmao @ilikethese @@ -57,17 +57,17 @@ CMakeLists.txt @ilikethese @etkmao /renderer/tdf/ios/ @wwwcg @ruifanyuan # renderer: voltron -/renderer/voltron/ @lvfen @skindhu +/renderer/voltron/ @lvfen @henryjin0511 # module: vfs /modules/vfs/ @etkmao @ilikethese /modules/vfs/android/ @siguangli @iPel /modules/vfs/android/**/src/main/cpp/ @etkmao @ilikethese /modules/vfs/ios/ @wwwcg @ruifanyuan -/modules/vfs/voltron/ @lvfen @skindhu +/modules/vfs/voltron/ @lvfen @henryjin0511 # module: voltron -/modules/voltron/ @lvfen @skindhu +/modules/voltron/ @lvfen @henryjin0511 # module: android /modules/android/ @siguangli @iPel @@ -90,6 +90,7 @@ CMakeLists.txt @ilikethese @etkmao /framework/examples/android-demo/res/ @zealotchen0 /framework/examples/ios-demo/ @wwwcg @ruifanyuan /framework/examples/ios-demo/res/ @zealotchen0 +/framework/examples/voltron-demo/ @henryjin0511 # doc: pages /*.md @zealotchen0 diff --git a/docs/api/hippy-react/components.md b/docs/api/hippy-react/components.md index ff3a957f12a..80cc3a9fa49 100644 --- a/docs/api/hippy-react/components.md +++ b/docs/api/hippy-react/components.md @@ -97,7 +97,6 @@ import icon from './qb_icon_new.png'; | 参数 | 描述 | 类型 | 支持平台 | | --------------------- | ------------------------------------------------------------ | ----------------------------------------------------------- | -------- | | bounces | 是否开启回弹效果,默认 `true`, Android `2.14.1` 版本后支持该属性,老版本使用 `overScrollEnabled` | `boolean` | `Android`、`iOS`、`Voltron` | -| overScrollEnabled | 是否开启回弹效果,默认 `true`,3.0 版本后即将废弃 | `boolean` | `Android、Voltron` | | getRowKey | 指定一个函数,在其中返回对应条目的 Key 值,详见 [React 官文](//reactjs.org/docs/lists-and-keys.html) | `(index: number) => any` | `Android、iOS、hippy-react-web、Web-Renderer、Voltron` | | getRowStyle | 设置 `ListViewItem` 容器的样式。当设置了 `horizontal=true` 启用横向 `ListView` 时,需显式设置 `ListViewItem` 宽度 | `(index: number) => styleObject` | `Android、iOS、hippy-react-web、Web-Renderer、Voltron` | | getHeaderStyle | 设置 `PullHeader` 容器的样式。当设置了 `horizontal=true` 启用横向 `ListView` 时,需显式设置 `PullHeader` 宽度。`最低支持版本2.14.1` | `() => styleObject` | `Android、iOS、Voltron` | @@ -176,7 +175,6 @@ import icon from './qb_icon_new.png'; | 参数 | 描述 | 类型 | 支持平台 | | --------------------- | ------------------------------------------------------------ | ------------------------------------------------------------ | -------- | -| animated | 弹出时是否需要带动画 | `boolean` | `Android、iOS、hippy-react-web、Web-Renderer、Voltron` | | animationType | 动画效果 | `enum (none, slide, fade, slide_fade)` | `Android、iOS、hippy-react-web、Web-Renderer、Voltron` | | supportedOrientations | 支持屏幕翻转方向 | `enum (portrait, portrait-upside-down, landscape, landscape-left, landscape-right)[]` | `iOS` | | immersionStatusBar | 是否是沉浸式状态栏。`default: false` | `boolean` | `Android、Voltron` | diff --git a/docs/api/hippy-react/modules.md b/docs/api/hippy-react/modules.md index 1c26bfff211..67acc1194d0 100644 --- a/docs/api/hippy-react/modules.md +++ b/docs/api/hippy-react/modules.md @@ -159,7 +159,7 @@ AsyncStorage 是一个简单的、异步的、持久化的 Key-Value 存储系 ### AsyncStorage.multiGet -`(key: string[]) => Promise<[key: string, value: value][]>` 一次性用多个 key 值的数组去批量请求缓存数据,返回值将在回调函数以键值对的二维数组形式返回。 +`(key: string[]) => Promise<[key: string, value: string][]>` 一次性用多个 key 值的数组去批量请求缓存数据,返回值将在回调函数以键值对的二维数组形式返回。 > - key: string[] - 需要获取值的目标 key 数组 @@ -171,9 +171,9 @@ AsyncStorage 是一个简单的、异步的、持久化的 Key-Value 存储系 ### AsyncStorage.multiSet -`(keyValuePairs: [key: string, value: value][]) => void` 调用这个函数可以批量存储键值对对象。 +`(keyValuePairs: [key: string, value: string][]) => void` 调用这个函数可以批量存储键值对对象。 -> - keyValuePairs: [key: string, value: value][] - 需要设置的储键值二维数组 +> - keyValuePairs: [key: string, value: string][] - 需要设置的储键值二维数组 ### AsyncStorage.removeItem @@ -256,7 +256,7 @@ AsyncStorage 是一个简单的、异步的、持久化的 Key-Value 存储系 `(target: 'window' | 'screen') => { height: number, width: number, scale: number, statusBarHeight, navigatorBarHeight }` Hippy Root View 尺寸或者屏幕尺寸。 > - target: 'window' | 'screen' - 指定丈量 Hippy Root View 或者屏幕尺寸。 -> - Android 特别说明:因为历史遗留问题,screen 下的 statusBarHeight 是按实际像素算的,window 下经过修正已经是 dp 单位。 +> - Android 特别说明:因为历史遗留问题,2.x 及以下版本的 screen 下的 statusBarHeight 是按物理像素算的,window 下经过修正已经是 dp 单位;3.0 及以上版本 screen 和 window 均为 dp 单位。 > - navigatorBarHeight: Android 底部 navigatorBar 高度;最低支持版本 2.3.4 --- diff --git a/docs/api/hippy-vue/components.md b/docs/api/hippy-vue/components.md index 55da7fdfd18..f672300e187 100644 --- a/docs/api/hippy-vue/components.md +++ b/docs/api/hippy-vue/components.md @@ -344,14 +344,13 @@ Hippy 的重点功能,高性能的可复用列表组件,在终端侧会被 | --------------------- | ------------------------------------------------------------ | ----------------------------------------------------------- | -------- | | horizontal | 指定 `ul` 是否采用横向布局。`default: undefined` 纵向布局,Android `2.14.1` 版本后可设置 `false` 显式固定纵向布局;iOS 暂不支持横向 `ul` | `boolean \| undefined` | `Android、Voltron` | | initialContentOffset | 初始位移值。在列表初始化时即可指定滚动距离,避免初始化后再通过 scrollTo 系列方法产生的闪动。Android 在 `2.8.0` 版本后支持 | `number` | `Android、iOS、Web-Renderer、Voltron` | -| bounces | 是否开启回弹效果,默认 `true` | `boolean` | `iOS、Voltron` | -| overScrollEnabled | 是否开启回弹效果,默认 `true` | `boolean` | `Android、Voltron` | -| rowShouldSticky | 设置 `ul` 是否需要开启悬停效果能力,与 `li` 的 `sticky` 配合使用。 `default: false` | `boolean` | `Android、iOS、Web-Renderer、Voltron` +| bounces | 是否开启回弹效果,默认 `true`, Android `2.14.1` 版本后支持该属性,老版本使用 `overScrollEnabled` | `boolean` | `Android、iOS、Voltron` | +| rowShouldSticky | 设置 `ul` 是否需要开启悬停效果能力,与 `li` 的 `sticky` 配合使用。 `default: false` | `boolean` | `Android、iOS、Web-Renderer、Voltron`| | scrollEnabled | 滑动是否开启。`default: true` | `boolean` | `Android、iOS、Web-Renderer、Voltron` | | scrollEventThrottle | 指定滑动事件的回调频率,传入数值指定了多少毫秒(ms)组件会调用一次 `onScroll` 回调事件,默认 200ms | `number` | `Android、iOS、Web-Renderer、Voltron` | | showScrollIndicator | 是否显示滚动条。`default: true` | `boolean` | `iOS、Voltron` | | preloadItemNumber | 指定当列表滚动至倒数第几行时触发 `endReached` 回调。 | `number` | `Android、iOS、Web-Renderer、Voltron` | -| exposureEventEnabled | Android 曝光能力启用开关,如果要使用 `appear`、`disappear` 相关事件,Android 需要设置该开关(iOS无需设置), `default: true` | `boolean` | `Android、Voltron` +| exposureEventEnabled | Android 曝光能力启用开关,如果要使用 `appear`、`disappear` 相关事件,Android 需要设置该开关(iOS无需设置), `default: true` | `boolean` | `Android、Voltron`| | endReached | 当所有的数据都已经渲染过,并且列表被滚动到最后一条时,将触发 `endReached` 回调。 | `Function` | `Android、iOS、Web-Renderer、Voltron` | | editable | 是否可编辑,开启侧滑删除时需要设置为 `true`。`最低支持版本2.9.0` | `boolean` | `iOS` | | delText | 侧滑删除文本。`最低支持版本2.9.0` | `string` | `iOS` | diff --git a/docs/api/hippy-vue/vue-native.md b/docs/api/hippy-vue/vue-native.md index 6206b60cb25..da43ca625b4 100644 --- a/docs/api/hippy-vue/vue-native.md +++ b/docs/api/hippy-vue/vue-native.md @@ -120,7 +120,7 @@ Vue.Native.AsyncStorage.getItem('itemKey'); ### AsyncStorage.multiGet -`(key: string[]) => Promise<[key: string, value: value][]>` 一次性用多个 key 值的数组去批量请求缓存数据,返回值将在回调函数以键值对的二维数组形式返回。 +`(key: string[]) => Promise<[key: string, value: string][]>` 一次性用多个 key 值的数组去批量请求缓存数据,返回值将在回调函数以键值对的二维数组形式返回。 > * key: string[] - 需要获取值的目标 key 数组 @@ -132,9 +132,9 @@ Vue.Native.AsyncStorage.getItem('itemKey'); ### AsyncStorage.multiSet -`(keyValuePairs: [key: string, value: value][]) => void` 调用这个函数可以批量存储键值对对象。 +`(keyValuePairs: [key: string, value: string][]) => void` 调用这个函数可以批量存储键值对对象。 -> * keyValuePairs: [key: string, value: value][] - 需要设置的储键值二维数组 +> * keyValuePairs: [key: string, value: string][] - 需要设置的储键值二维数组 ### AsyncStorage.removeItem diff --git a/dom/src/dom/dom_node.cc b/dom/src/dom/dom_node.cc index 701db433487..96c1a33d09f 100644 --- a/dom/src/dom/dom_node.cc +++ b/dom/src/dom/dom_node.cc @@ -322,13 +322,9 @@ LayoutResult DomNode::GetLayoutInfoFromRoot() { void DomNode::TransferLayoutOutputsRecursive(std::vector>& changed_nodes) { auto not_equal = std::not_equal_to<>(); bool changed = layout_node_->IsDirty() || layout_node_->HasNewLayout(); -#ifdef __ANDROID__ - bool trigger_layout_event = true; -#else bool trigger_layout_event = not_equal(layout_.left, layout_node_->GetLeft()) || not_equal(layout_.top, layout_node_->GetTop()) || not_equal(layout_.width, layout_node_->GetWidth()) || not_equal(layout_.height, layout_node_->GetHeight()); -#endif layout_.left = layout_node_->GetLeft(); layout_.top = layout_node_->GetTop(); @@ -611,6 +607,13 @@ std::ostream& operator<<(std::ostream& os, const RefInfo& ref_info) { return os; } +std::ostream& operator<<(std::ostream& os, const DiffInfo& diff_info) { + os << "{"; + os << "\"skip_style_diff\": " << diff_info.skip_style_diff << ", "; + os << "}"; + return os; +} + std::ostream& operator<<(std::ostream& os, const DomNode& dom_node) { os << "{"; os << "\"id\": " << dom_node.id_ << ", "; @@ -637,10 +640,14 @@ std::ostream& operator<<(std::ostream& os, const DomNode& dom_node) { std::ostream& operator<<(std::ostream& os, const DomInfo& dom_info) { auto dom_node = dom_info.dom_node; auto ref_info = dom_info.ref_info; + auto diff_info = dom_info.diff_info; os << "{"; if (ref_info != nullptr) { os << "\"ref info\": " << *ref_info << ", "; } + if (diff_info != nullptr) { + os << "\"diff info\": " << *diff_info << ", "; + } if (dom_node != nullptr) { os << "\"dom node\": " << *dom_node << ", "; } diff --git a/dom/src/dom/root_node.cc b/dom/src/dom/root_node.cc index 5032eb5bdf7..06459ad6f4e 100644 --- a/dom/src/dom/root_node.cc +++ b/dom/src/dom/root_node.cc @@ -157,7 +157,9 @@ void RootNode::UpdateDomNodes(std::vector>&& nodes) { if (!ext_update->empty()) { diff_value->insert(ext_update->begin(), ext_update->end()); } - dom_node->SetStyleMap(node_info->dom_node->GetStyleMap()); + if (!skip_style_diff) { + dom_node->SetStyleMap(node_info->dom_node->GetStyleMap()); + } dom_node->SetExtStyleMap(node_info->dom_node->GetExtStyle()); dom_node->SetDiffStyle(diff_value); @@ -276,21 +278,19 @@ void RootNode::CallFunction(uint32_t id, const std::string& name, const DomArgum } void RootNode::SyncWithRenderManager(const std::shared_ptr& render_manager) { - HP_PERF_LOG("RootNode::SyncWithRenderManager"); - unsigned long cnt = dom_operations_.size(); + TDF_PERF_DO_STMT_AND_LOG(unsigned long domCnt = dom_operations_.size(); , "RootNode::SyncWithRenderManager"); FlushDomOperations(render_manager); - HP_PERF_LOG("RootNode::FlushDomOperations Done, dom op count:%lld", cnt); - cnt = event_operations_.size(); + TDF_PERF_DO_STMT_AND_LOG(unsigned long evCnt = event_operations_.size(); , "RootNode::FlushDomOperations Done, dom op count:%lld", domCnt); FlushEventOperations(render_manager); - HP_PERF_LOG("RootNode::FlushEventOperations Done, event op count:%d",cnt); + TDF_PERF_LOG("RootNode::FlushEventOperations Done, event op count:%d", evCnt); DoAndFlushLayout(render_manager); - HP_PERF_LOG("RootNode::DoAndFlushLayout Done"); + TDF_PERF_LOG("RootNode::DoAndFlushLayout Done"); auto dom_manager = dom_manager_.lock(); if (dom_manager) { dom_manager->RecordDomEndTimePoint(); } render_manager->EndBatch(GetWeakSelf()); - HP_PERF_LOG("RootNode::SyncWithRenderManager End"); + TDF_PERF_LOG("RootNode::SyncWithRenderManager End"); } void RootNode::AddEvent(uint32_t id, const std::string& event_name) { diff --git a/driver/js/src/modules/animation_module.cc b/driver/js/src/modules/animation_module.cc index 7146ffa178c..9c26a60f154 100644 --- a/driver/js/src/modules/animation_module.cc +++ b/driver/js/src/modules/animation_module.cc @@ -166,6 +166,7 @@ std::shared_ptr ParseAnimation(const std::shared_ptr& const std::shared_ptr arguments[], std::shared_ptr& exception) { if (argument_count != kAnimationUpdateArgc) { + exception = context->CreateException("animation argument count error"); return nullptr; } @@ -306,11 +307,13 @@ RegisterAnimation(const std::weak_ptr& weak_scope) { auto weak_dom_manager = scope->GetDomManager(); auto dom_manager = weak_dom_manager.lock(); if (!dom_manager) { + exception = scope->GetContext()->CreateException("dom_manager null error"); return nullptr; } auto weak_root_node = scope->GetRootNode(); auto root_node = weak_root_node.lock(); if (!root_node) { + exception = scope->GetContext()->CreateException("root_node null error"); return nullptr; } auto result = ParseAnimation(scope->GetContext(), argument_count, arguments, exception); @@ -619,15 +622,18 @@ RegisterAnimationSet(const std::weak_ptr& weak_scope) { auto weak_dom_manager = scope->GetDomManager(); auto dom_manager = weak_dom_manager.lock(); if (!dom_manager) { + exception = scope->GetContext()->CreateException("dom_manager null error"); return nullptr; } auto weak_root_node = scope->GetRootNode(); auto root_node = weak_root_node.lock(); if (!root_node) { + exception = scope->GetContext()->CreateException("root_node null error"); return nullptr; } auto animation_manager = root_node->GetAnimationManager(); if (!animation_manager) { + exception = scope->GetContext()->CreateException("animation_manager null error"); return nullptr; } auto set = ParseAnimationSet(scope->GetContext(), argument_count, arguments, exception); diff --git a/driver/js/src/modules/scene_builder_module.cc b/driver/js/src/modules/scene_builder_module.cc index 1167b3dd342..661015c0df9 100644 --- a/driver/js/src/modules/scene_builder_module.cc +++ b/driver/js/src/modules/scene_builder_module.cc @@ -600,14 +600,14 @@ std::shared_ptr> RegisterSceneBuilder(const std::wea size_t argument_count, const std::shared_ptr arguments[], std::shared_ptr&) -> std::shared_ptr { - HP_PERF_LOG("SceneBuilder.build()"); + TDF_PERF_LOG("SceneBuilder.build()"); auto scope = weak_scope.lock(); if (!scope) { - HP_PERF_LOG("SceneBuilder.build() exit with error"); + TDF_PERF_LOG("SceneBuilder.build() exit with error"); return nullptr; } SceneBuilder::Build(scope->GetDomManager(), scope->GetRootNode()); - HP_PERF_LOG("SceneBuilder.build() End"); + TDF_PERF_LOG("SceneBuilder.build() End"); return nullptr; }; class_template.functions.emplace_back(std::move(build_func_def)); diff --git a/framework/android/src/main/java/com/tencent/mtt/hippy/modules/nativemodules/network/NetworkModule.java b/framework/android/src/main/java/com/tencent/mtt/hippy/modules/nativemodules/network/NetworkModule.java index 77ba1d25862..945e1cc31fa 100644 --- a/framework/android/src/main/java/com/tencent/mtt/hippy/modules/nativemodules/network/NetworkModule.java +++ b/framework/android/src/main/java/com/tencent/mtt/hippy/modules/nativemodules/network/NetworkModule.java @@ -34,7 +34,6 @@ import com.tencent.mtt.hippy.modules.nativemodules.HippyNativeModuleBase; import com.tencent.mtt.hippy.runtime.builtins.JSObject; import com.tencent.vfs.ResourceDataHolder; -import com.tencent.vfs.ResourceDataHolder.TransferType; import com.tencent.vfs.VfsManager; import com.tencent.vfs.VfsManager.FetchResourceCallback; import java.nio.charset.StandardCharsets; @@ -54,7 +53,7 @@ public NetworkModule(HippyEngineContext context) { } @SuppressWarnings("deprecation") - private void normalizeRequestHeaders(@NonNull HippyMap headers, + protected void normalizeRequestHeaders(@NonNull HippyMap headers, @NonNull HashMap requestHeaders) { Set> entrySet = headers.entrySet(); for (Entry entry : entrySet) { @@ -82,7 +81,7 @@ private void normalizeRequestHeaders(@NonNull HippyMap headers, } @SuppressWarnings("deprecation") - private void normalizeRequest(@NonNull HippyMap request, + protected void normalizeRequest(@NonNull HippyMap request, @NonNull HashMap requestHeaders, @NonNull HashMap requestParams) throws IllegalStateException { Set> entrySet = request.entrySet(); @@ -103,7 +102,7 @@ private void normalizeRequest(@NonNull HippyMap request, } } - private void handleFetchResponse(@NonNull ResourceDataHolder dataHolder, Promise promise) + protected void handleFetchResponse(@NonNull ResourceDataHolder dataHolder, Promise promise) throws IllegalStateException { JSObject responseObject = new JSObject(); int statusCode = 0; @@ -151,12 +150,12 @@ public void fetch(final HippyMap request, final Promise promise) { try { normalizeRequest(request, requestHeaders, requestParams); } catch (Exception e) { - promise.resolve(e.getMessage()); + promise.reject(e.getMessage()); return; } final String uri = requestParams.get(HTTP_URL); if (TextUtils.isEmpty(uri)) { - promise.resolve("Get url parameter failed!"); + promise.reject("Get url parameter failed!"); return; } vfsManager.fetchResourceAsync(uri, requestHeaders, requestParams, @@ -168,12 +167,13 @@ public void onFetchCompleted(@NonNull ResourceDataHolder dataHolder) { try { handleFetchResponse(dataHolder, promise); } catch (IllegalStateException e) { - promise.resolve( - "Handle response failed: " + dataHolder.errorMessage); + promise.reject( + "Handle response failed: " + e.getMessage()); } } else { - promise.resolve( - "Load remote resource failed: " + dataHolder.errorMessage); + String error = TextUtils.isEmpty(dataHolder.errorMessage) + ? "Load remote resource failed!" : dataHolder.errorMessage; + promise.resolve(error); } dataHolder.recycle(); } diff --git a/framework/examples/android-demo/src/main/java/com/openhippy/example/ExampleAPIProvider.kt b/framework/examples/android-demo/src/main/java/com/openhippy/example/ExampleAPIProvider.kt index 8ff63b8053a..2064f77e7b3 100644 --- a/framework/examples/android-demo/src/main/java/com/openhippy/example/ExampleAPIProvider.kt +++ b/framework/examples/android-demo/src/main/java/com/openhippy/example/ExampleAPIProvider.kt @@ -44,6 +44,6 @@ class ExampleAPIProvider : HippyAPIProvider { * register View controller for JavaScript */ override fun getControllers(): List>> { - return emptyList() + return arrayListOf(ExampleCustomPropsController::class.java) } -} \ No newline at end of file +} diff --git a/framework/examples/android-demo/src/main/java/com/openhippy/example/ExampleCustomPropsController.kt b/framework/examples/android-demo/src/main/java/com/openhippy/example/ExampleCustomPropsController.kt new file mode 100644 index 00000000000..c67b24aef65 --- /dev/null +++ b/framework/examples/android-demo/src/main/java/com/openhippy/example/ExampleCustomPropsController.kt @@ -0,0 +1,39 @@ +/* + * Tencent is pleased to support the open source community by making Hippy + * available. + * Copyright (C) 2018 THL A29 Limited, a Tencent company. All rights reserved. + * Licensed 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. + */ + +package com.openhippy.example + +import android.view.View +import com.tencent.mtt.hippy.annotation.HippyController +import com.tencent.mtt.hippy.annotation.HippyControllerProps +import com.tencent.mtt.hippy.common.HippyMap +import com.tencent.mtt.hippy.utils.LogUtils +import com.tencent.mtt.hippy.views.custom.HippyCustomPropsController + +@HippyController(name = HippyCustomPropsController.CLASS_NAME) +class ExampleCustomPropsController : HippyCustomPropsController() { + + val TAG = "ExampleCustomPropsController" + + @HippyControllerProps(name = "pageParams", defaultType = HippyControllerProps.MAP) + fun setDtPageParams(view: View, params: HippyMap?) { + LogUtils.d(TAG, "setDtPageParams id " + view.id + ", params " + params) + } + + @HippyControllerProps(name = "elementParams", defaultType = HippyControllerProps.MAP) + fun setDtElementParams(view: View, params: HippyMap?) { + LogUtils.d(TAG, "setDtElementParams id " + view.id + ", params " + params) + } +} diff --git a/framework/examples/ios-demo/HippyDemo/HomePage/HomePageViewController.mm b/framework/examples/ios-demo/HippyDemo/HomePage/HomePageViewController.mm index d2d479ec842..4a5f3900393 100644 --- a/framework/examples/ios-demo/HippyDemo/HomePage/HomePageViewController.mm +++ b/framework/examples/ios-demo/HippyDemo/HomePage/HomePageViewController.mm @@ -45,7 +45,7 @@ - (void)viewDidLoad { self.imageView.image = [UIImage imageFromIconName:@"first_page_logo"]; - self.verLabel.text = [NSString stringWithFormat:@"Ver:%@", HippySDKVersion]; + self.verLabel.text = [NSString stringWithFormat:@"Ver:%@", _HippySDKVersion]; self.buttonView.layer.shadowColor = [UIColor grayColor].CGColor; self.buttonView.layer.shadowOffset = CGSizeMake(0, -1); diff --git a/framework/examples/ios-demo/HippyDemo/Info.plist b/framework/examples/ios-demo/HippyDemo/Info.plist index 5f9fba50749..b381cdad8cd 100644 --- a/framework/examples/ios-demo/HippyDemo/Info.plist +++ b/framework/examples/ios-demo/HippyDemo/Info.plist @@ -10,8 +10,6 @@ $(PRODUCT_BUNDLE_IDENTIFIER) CFBundleInfoDictionaryVersion 6.0 - CFBundleName - $(PRODUCT_NAME) CFBundlePackageType $(PRODUCT_BUNDLE_PACKAGE_TYPE) CFBundleShortVersionString @@ -53,6 +51,6 @@ UIInterfaceOrientationLandscapeRight UIViewControllerBasedStatusBarAppearance - + diff --git a/framework/examples/ios-demo/HippyDemo/PageManager/HippyPageCacheContainerView.m b/framework/examples/ios-demo/HippyDemo/PageManager/HippyPageCacheContainerView.m index 068a4796c54..1e48bb0f6e9 100644 --- a/framework/examples/ios-demo/HippyDemo/PageManager/HippyPageCacheContainerView.m +++ b/framework/examples/ios-demo/HippyDemo/PageManager/HippyPageCacheContainerView.m @@ -24,7 +24,7 @@ #import "HippyPageCacheContainerView.h" #import "HippyPageCacheView.h" #import "HippyPageCache.h" -#import "HippyAsserts.h" +#import "HippyAssert.h" @interface HippyPageCacheContainerView () { __weak UIView *_lastPageCacheView; //never removed from superview diff --git a/framework/examples/ios-demo/HippyDemo/RenderPage/HippyDemoViewController.mm b/framework/examples/ios-demo/HippyDemo/RenderPage/HippyDemoViewController.mm index e513e09ea57..87183a5e03b 100644 --- a/framework/examples/ios-demo/HippyDemo/RenderPage/HippyDemoViewController.mm +++ b/framework/examples/ios-demo/HippyDemo/RenderPage/HippyDemoViewController.mm @@ -30,11 +30,38 @@ #import #import #import -#import +#import #import static NSString *const engineKey = @"Demo"; +static NSString *formatLog(NSDate *timestamp, HippyLogLevel level, NSString *fileName, NSNumber *lineNumber, NSString *message) { + static NSArray *logLevelMap = @[@"TRACE", @"INFO", @"WARN", @"ERROR", @"FATAL"]; + static NSDateFormatter *formatter; + static dispatch_once_t onceToken; + dispatch_once(&onceToken, ^{ + formatter = [NSDateFormatter new]; + formatter.dateFormat = formatter.dateFormat = @"yyyy-MM-dd HH:mm:ss.SSS"; + }); + + NSString *levelStr = level < 0 || level > logLevelMap.count ? logLevelMap[1] : logLevelMap[level]; + + if(fileName){ + return [[NSString alloc] initWithFormat:@"[%@][%@:%d][%@]%@", + [formatter stringFromDate:timestamp], + fileName.lastPathComponent, + lineNumber.intValue, + levelStr, + message + ]; + }else{ + return [[NSString alloc] initWithFormat:@"[%@]%@", + [formatter stringFromDate:timestamp], + message + ]; + } +} + @interface HippyDemoViewController () { DriverType _driverType; RenderType _renderType; @@ -99,7 +126,12 @@ - (void)viewDidLoad { - (void)registerLogFunction { HippySetLogFunction(^(HippyLogLevel level, HippyLogSource source, NSString *fileName, NSNumber *lineNumber, NSString *message) { - NSLog(@"hippy says:%@ in file %@ at line %@", message, fileName, lineNumber); + NSString *log = formatLog([NSDate date], level, fileName, lineNumber, message); + if([log hasSuffix:@"\n"]){ + fprintf(stderr, "%s", log.UTF8String); + }else{ + fprintf(stderr, "%s\n", log.UTF8String); + } }); } diff --git a/framework/examples/ios-demo/HippyDemo/TestModule.mm b/framework/examples/ios-demo/HippyDemo/TestModule.mm index 2e739cad97d..04879102526 100644 --- a/framework/examples/ios-demo/HippyDemo/TestModule.mm +++ b/framework/examples/ios-demo/HippyDemo/TestModule.mm @@ -30,7 +30,7 @@ #import "HippyRedBox.h" #import "DemoConfigs.h" #import "HippyMethodInterceptorProtocol.h" -#import "HippyAsserts.h" +#import "HippyAssert.h" static NSString *const engineKey = @"Demo"; diff --git a/framework/examples/ios-demo/podfile b/framework/examples/ios-demo/podfile index 1b27e1b7249..05ad4466a19 100644 --- a/framework/examples/ios-demo/podfile +++ b/framework/examples/ios-demo/podfile @@ -5,8 +5,8 @@ install! 'cocoapods', :deterministic_uuids => false, :generate_multiple_pod_projects => true -#use_frameworks! :linkage => :static # 静态库framework格式 -#ENV["use_frameworks"] = "true" # for hippy when set use_frameworks! +use_frameworks! :linkage => :static # 静态库framework格式 +ENV["use_frameworks"] = "true" # for hippy when set use_frameworks! workspace 'HippyDemo.xcworkspace' diff --git a/framework/examples/voltron-demo/IOSProj/IOSProj.xcodeproj/project.pbxproj b/framework/examples/voltron-demo/IOSProj/IOSProj.xcodeproj/project.pbxproj index 79545b010d9..54a372cf9f9 100644 --- a/framework/examples/voltron-demo/IOSProj/IOSProj.xcodeproj/project.pbxproj +++ b/framework/examples/voltron-demo/IOSProj/IOSProj.xcodeproj/project.pbxproj @@ -3,7 +3,7 @@ archiveVersion = 1; classes = { }; - objectVersion = 56; + objectVersion = 55; objects = { /* Begin PBXBuildFile section */ @@ -229,7 +229,7 @@ }; }; buildConfigurationList = C2DC03F32A3EE271004B7147 /* Build configuration list for PBXProject "IOSProj" */; - compatibilityVersion = "Xcode 14.0"; + compatibilityVersion = "Xcode 13.0"; developmentRegion = en; hasScannedForEncodings = 0; knownRegions = ( diff --git a/framework/examples/voltron-demo/android-proj/app/src/main/java/com/example/android_proj/MainActivity.java b/framework/examples/voltron-demo/android-proj/app/src/main/java/com/example/android_proj/MainActivity.java index fb6e8186e71..8c8e52cde26 100644 --- a/framework/examples/voltron-demo/android-proj/app/src/main/java/com/example/android_proj/MainActivity.java +++ b/framework/examples/voltron-demo/android-proj/app/src/main/java/com/example/android_proj/MainActivity.java @@ -22,17 +22,35 @@ import android.view.View; import android.widget.Button; +import java.util.concurrent.atomic.AtomicLong; + +import io.flutter.Log; import io.flutter.embedding.android.FlutterActivity; import io.flutter.embedding.engine.FlutterEngine; import io.flutter.embedding.engine.FlutterEngineCache; import io.flutter.embedding.engine.dart.DartExecutor; public class MainActivity extends AppCompatActivity { + private final AtomicLong idCounter = new AtomicLong(0); + private long currentEngineId; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); + createNewEngine(); + + Button button = findViewById(R.id.button); + button.setOnClickListener(new View.OnClickListener() { + @Override + public void onClick(View view) { + openWithNewEngine(); + } + }); + } + + void createNewEngine() { + currentEngineId = idCounter.incrementAndGet(); // Instantiate a FlutterEngine. FlutterEngine flutterEngine = new FlutterEngine(this); @@ -45,16 +63,16 @@ protected void onCreate(Bundle savedInstanceState) { // Cache the FlutterEngine to be used by FlutterActivity. FlutterEngineCache .getInstance() - .put("my_engine_id", flutterEngine); + .put("my_engine_id_" + currentEngineId, flutterEngine); + } - Button button = findViewById(R.id.button); - button.setOnClickListener(new View.OnClickListener() { - @Override - public void onClick(View view) { - startActivity( - FlutterActivity.withCachedEngine("my_engine_id").destroyEngineWithActivity(true).build(MainActivity.this) - ); - } - }); + void openWithNewEngine() { + startActivity( + FlutterActivity + .withCachedEngine("my_engine_id_" + currentEngineId) + .destroyEngineWithActivity(true) + .build(MainActivity.this) + ); + createNewEngine(); } } diff --git a/framework/examples/voltron-demo/flutter_module/lib/base_voltron_page.dart b/framework/examples/voltron-demo/flutter_module/lib/base_voltron_page.dart index ab28a3782f2..5b309a321d9 100644 --- a/framework/examples/voltron-demo/flutter_module/lib/base_voltron_page.dart +++ b/framework/examples/voltron-demo/flutter_module/lib/base_voltron_page.dart @@ -159,6 +159,7 @@ class _BaseVoltronPageState extends State { initParams.coreJSAssetsPath = _coreBundle; initParams.codeCacheTag = "common"; } + initParams.integratedMode = IntegratedMode.flutterModule; initParams.providers = [ MyAPIProvider(), ]; diff --git a/framework/examples/voltron-demo/flutter_module/pubspec.yaml b/framework/examples/voltron-demo/flutter_module/pubspec.yaml index 71fef2d1378..f84d76840b8 100644 --- a/framework/examples/voltron-demo/flutter_module/pubspec.yaml +++ b/framework/examples/voltron-demo/flutter_module/pubspec.yaml @@ -45,8 +45,7 @@ environment: dependencies: flutter: sdk: flutter - voltron: - path: ../../../voltron + voltron: 0.0.37 qr_flutter: 4.0.0 # The following adds the Cupertino Icons font to your application. diff --git a/framework/examples/voltron-demo/flutter_proj/lib/base_voltron_page.dart b/framework/examples/voltron-demo/flutter_proj/lib/base_voltron_page.dart index b185b49254a..405240ee21f 100644 --- a/framework/examples/voltron-demo/flutter_proj/lib/base_voltron_page.dart +++ b/framework/examples/voltron-demo/flutter_proj/lib/base_voltron_page.dart @@ -158,6 +158,7 @@ class _BaseVoltronPageState extends State { initParams.coreJSAssetsPath = _coreBundle; initParams.codeCacheTag = "common"; } + initParams.integratedMode = IntegratedMode.flutterApp; initParams.providers = [ MyAPIProvider(), ]; diff --git a/framework/examples/voltron-demo/flutter_proj/pubspec.yaml b/framework/examples/voltron-demo/flutter_proj/pubspec.yaml index 696b2bd078b..1a3b78d5552 100644 --- a/framework/examples/voltron-demo/flutter_proj/pubspec.yaml +++ b/framework/examples/voltron-demo/flutter_proj/pubspec.yaml @@ -63,8 +63,7 @@ dev_dependencies: flutter_test: sdk: flutter - voltron: ^0.0.29 - + voltron: 0.0.37 qr_flutter: 4.0.0 # The "flutter_lints" package below contains a set of recommended lints to diff --git a/framework/ios/base/HippyDeviceBaseInfo.mm b/framework/ios/base/HippyDeviceBaseInfo.mm index 3e7fbcd18e9..ec6b4776cad 100644 --- a/framework/ios/base/HippyDeviceBaseInfo.mm +++ b/framework/ios/base/HippyDeviceBaseInfo.mm @@ -21,7 +21,7 @@ */ #import -#import "HippyAsserts.h" +#import "HippyAssert.h" #import "HippyUtils.h" #import "HippyDeviceBaseInfo.h" #import "HippyEventDispatcher.h" diff --git a/framework/ios/base/HippyDisplayLink.m b/framework/ios/base/HippyDisplayLink.m index ca12b232eb5..c4be3f76b8e 100644 --- a/framework/ios/base/HippyDisplayLink.m +++ b/framework/ios/base/HippyDisplayLink.m @@ -25,7 +25,7 @@ #import #import -#import "HippyAsserts.h" +#import "HippyAssert.h" #import "HippyBridgeModule.h" #import "HippyFrameUpdate.h" #import "HippyModuleData.h" diff --git a/framework/ios/base/HippyKeyCommands.m b/framework/ios/base/HippyKeyCommands.m index 7314fa61368..e7a33ee1f17 100644 --- a/framework/ios/base/HippyKeyCommands.m +++ b/framework/ios/base/HippyKeyCommands.m @@ -22,7 +22,7 @@ #import "HippyKeyCommands.h" #import -#import "HippyAsserts.h" +#import "HippyAssert.h" #import "HippyDefines.h" #import "HippyUtils.h" diff --git a/framework/ios/base/bridge/HippyBridge.h b/framework/ios/base/bridge/HippyBridge.h index 4be77018336..27ed97e07b6 100644 --- a/framework/ios/base/bridge/HippyBridge.h +++ b/framework/ios/base/bridge/HippyBridge.h @@ -54,8 +54,9 @@ NS_ASSUME_NONNULL_BEGIN /** * Indicate hippy sdk version + * 注意:为兼容2.0版本,保持的相同的下划线前缀命名,不可修改 */ -HIPPY_EXTERN NSString *const HippySDKVersion; +HIPPY_EXTERN NSString *const _HippySDKVersion; /** * This notification triggers a reload of all bridges currently running. * Deprecated, use HippyBridge::requestReload instead. diff --git a/framework/ios/base/bridge/HippyBridge.mm b/framework/ios/base/bridge/HippyBridge.mm index a864e4bf0d2..7965ff25245 100644 --- a/framework/ios/base/bridge/HippyBridge.mm +++ b/framework/ios/base/bridge/HippyBridge.mm @@ -39,7 +39,7 @@ #import "HippyRedBox.h" #import "HippyTurboModule.h" #import "HippyUtils.h" -#import "HippyAsserts.h" +#import "HippyAssert.h" #import "HippyConvert.h" #import "HippyDefaultImageProvider.h" #import "HippyI18nUtils.h" @@ -79,7 +79,7 @@ NSString *const HippyJavaScriptDidLoadNotification = @"HippyJavaScriptDidLoadNotification"; NSString *const HippyJavaScriptDidFailToLoadNotification = @"HippyJavaScriptDidFailToLoadNotification"; NSString *const HippyDidInitializeModuleNotification = @"HippyDidInitializeModuleNotification"; -NSString *const HippySDKVersion = @"unspecified"; +NSString *const _HippySDKVersion = @HIPPY_STR(HIPPY_VERSION); static NSString *const HippyNativeGlobalKeyOS = @"OS"; @@ -414,7 +414,7 @@ - (void)loadBundleURL:(NSURL *)bundleURL } return; } - HippyLogInfo(@"Begin loading bundle(%s) at %s", HP_CSTR_NOT_NULL(bundleURL.absoluteString.lastPathComponent.UTF8String), HP_CSTR_NOT_NULL(bundleURL.absoluteString.UTF8String)); + HippyLogInfo(@"[HP PERF] Begin loading bundle(%s) at %s", HP_CSTR_NOT_NULL(bundleURL.absoluteString.lastPathComponent.UTF8String), HP_CSTR_NOT_NULL(bundleURL.absoluteString.UTF8String)); [_bundleURLs addObject:bundleURL]; dispatch_async(HippyBridgeQueue(), ^{ [self beginLoadingBundle:bundleURL completion:completion]; @@ -509,15 +509,15 @@ - (void)loadInstanceForRootView:(NSNumber *)rootTag withProperties:(NSDictionary - (void)innerLoadInstanceForRootView:(NSNumber *)rootTag withProperties:(NSDictionary *)props { HippyAssert(_moduleName, @"module name must not be null"); HippyLogInfo(@"[Hippy_OC_Log][Life_Circle],Running application %@ (%@)", _moduleName, props); - HippyLogInfo(@"Begin loading instance for HippyBridge(%p)", self); + HippyLogInfo(@"[HP PERF] Begin loading instance for HippyBridge(%p)", self); NSDictionary *param = @{@"name": _moduleName, @"id": rootTag, @"params": props ?: @{}, - @"version": HippySDKVersion}; + @"version": _HippySDKVersion}; footstone::value::HippyValue value = [param toHippyValue]; std::shared_ptr domValue = std::make_shared(value); self.javaScriptExecutor.pScope->LoadInstance(domValue); - HippyLogInfo(@"End loading instance for HippyBridge(%p)", self); + HippyLogInfo(@"[HP PERF] End loading instance for HippyBridge(%p)", self); } - (void)rootViewSizeChangedEvent:(NSNumber *)tag params:(NSDictionary *)params { @@ -991,7 +991,7 @@ - (NSDictionary *)genRawDeviceInfoDict { [deviceInfo setValue:@"ios" forKey:HippyNativeGlobalKeyOS]; [deviceInfo setValue:iosVersion forKey:HippyNativeGlobalKeyOSVersion]; [deviceInfo setValue:deviceModel forKey:HippyNativeGlobalKeyDevice]; - [deviceInfo setValue:HippySDKVersion forKey:HippyNativeGlobalKeySDKVersion]; + [deviceInfo setValue:_HippySDKVersion forKey:HippyNativeGlobalKeySDKVersion]; NSString *appVer = [[NSBundle.mainBundle infoDictionary] objectForKey:@"CFBundleShortVersionString"]; if (appVer) { diff --git a/framework/ios/base/enginewrapper/jsc/HippyJSCContextWrapper.mm b/framework/ios/base/enginewrapper/jsc/HippyJSCContextWrapper.mm index 538f5e53fe5..d11a66c6ce5 100644 --- a/framework/ios/base/enginewrapper/jsc/HippyJSCContextWrapper.mm +++ b/framework/ios/base/enginewrapper/jsc/HippyJSCContextWrapper.mm @@ -23,7 +23,7 @@ #import #import "NSObject+JSValue.h" -#import "HippyAsserts.h" +#import "HippyAssert.h" #import "HippyJSCContextWrapper.h" #import "HippyJSStackFrame.h" diff --git a/framework/ios/base/executors/HippyJSExecutor.mm b/framework/ios/base/executors/HippyJSExecutor.mm index 2dd25e4e8db..d395cef9e67 100644 --- a/framework/ios/base/executors/HippyJSExecutor.mm +++ b/framework/ios/base/executors/HippyJSExecutor.mm @@ -22,7 +22,7 @@ #import #import "VFSUriHandler.h" -#import "HippyAsserts.h" +#import "HippyAssert.h" #import "HippyBundleURLProvider.h" #import "HippyContextWrapper.h" #import "HippyDefines.h" diff --git a/framework/ios/base/modules/HippyEventDispatcher.mm b/framework/ios/base/modules/HippyEventDispatcher.mm index 029ad65cf73..5ce0fffa484 100644 --- a/framework/ios/base/modules/HippyEventDispatcher.mm +++ b/framework/ios/base/modules/HippyEventDispatcher.mm @@ -21,7 +21,7 @@ */ #import "HippyEventDispatcher.h" -#import "HippyAsserts.h" +#import "HippyAssert.h" #import "HippyUtils.h" const NSInteger HippyTextUpdateLagWarningThreshold = 3; diff --git a/framework/ios/base/modules/HippyModuleData.mm b/framework/ios/base/modules/HippyModuleData.mm index 02c53834502..e96494c7647 100644 --- a/framework/ios/base/modules/HippyModuleData.mm +++ b/framework/ios/base/modules/HippyModuleData.mm @@ -23,7 +23,7 @@ #import "HippyModuleData.h" #import "HippyBridge.h" #import "HippyModuleMethod.h" -#import "HippyAsserts.h" +#import "HippyAssert.h" #import "HippyLog.h" #import "HippyUtils.h" diff --git a/framework/ios/base/modules/HippyModuleMethod.mm b/framework/ios/base/modules/HippyModuleMethod.mm index f753230df47..903500d60e5 100644 --- a/framework/ios/base/modules/HippyModuleMethod.mm +++ b/framework/ios/base/modules/HippyModuleMethod.mm @@ -24,7 +24,7 @@ #import "HippyBridge.h" #import "HippyTurboModuleManager.h" #import "HippyUtils.h" -#import "HippyAsserts.h" +#import "HippyAssert.h" #import "HippyConvert.h" #import "HippyLog.h" #import "HippyParserUtils.h" diff --git a/framework/ios/base/modules/HippyModulesSetup.mm b/framework/ios/base/modules/HippyModulesSetup.mm index 79dc84cb8b2..5740ab567dd 100644 --- a/framework/ios/base/modules/HippyModulesSetup.mm +++ b/framework/ios/base/modules/HippyModulesSetup.mm @@ -20,7 +20,7 @@ * limitations under the License. */ -#import "HippyAsserts.h" +#import "HippyAssert.h" #import "HippyBridge.h" #import "HippyModuleData.h" #import "HippyModulesSetup.h" @@ -176,7 +176,7 @@ - (void)setupModulesCompletion:(dispatch_block_t)completion { // Check for name collisions between preregistered modules HippyModuleData *moduleData = moduleDataByName[moduleName]; if (moduleData) { - HippyLogError(@"Attempted to register HippyBridgeModule class %@ for the " + HippyLogWarn(@"Attempted to register HippyBridgeModule class %@ for the " "name '%@', but name was already registered by class %@", moduleClass, moduleName, moduleData.moduleClass); continue; @@ -202,7 +202,7 @@ - (void)setupModulesCompletion:(dispatch_block_t)completion { continue; } else if ([moduleData.moduleClass new] != nil) { // Both modules were non-nil, so it's unclear which should take precedence - HippyLogError(@"Attempted to register HippyBridgeModule class %@ for the " + HippyLogWarn(@"Attempted to register HippyBridgeModule class %@ for the " "name '%@', but name was already registered by class %@", moduleClass, moduleName, moduleData.moduleClass); } diff --git a/framework/ios/debug/websocket/HippySRWebSocket.m b/framework/ios/debug/websocket/HippySRWebSocket.m index d05094e0c41..eafd9a1fab2 100644 --- a/framework/ios/debug/websocket/HippySRWebSocket.m +++ b/framework/ios/debug/websocket/HippySRWebSocket.m @@ -22,7 +22,7 @@ #import -#import "HippyAsserts.h" +#import "HippyAssert.h" #import "HippyLog.h" #import "HippySRSIMDHelpers.h" diff --git a/framework/ios/module/dev/HippyDevMenu.mm b/framework/ios/module/dev/HippyDevMenu.mm index 733e87e18e6..b04786630a2 100644 --- a/framework/ios/module/dev/HippyDevMenu.mm +++ b/framework/ios/module/dev/HippyDevMenu.mm @@ -24,7 +24,7 @@ #import "HippyEventDispatcher.h" #import "HippyKeyCommands.h" #import "HippyWebSocketProxy.h" -#import "HippyAsserts.h" +#import "HippyAssert.h" #import "HippyUtils.h" #import "HippyDefines.h" diff --git a/framework/ios/module/dev/HippyRedBox.mm b/framework/ios/module/dev/HippyRedBox.mm index f90215a110e..5f4d77b5695 100644 --- a/framework/ios/module/dev/HippyRedBox.mm +++ b/framework/ios/module/dev/HippyRedBox.mm @@ -25,7 +25,7 @@ #import "HippyRedBox.h" #import "HippyUtils.h" #import "HippyWeakProxy.h" -#import "HippyAsserts.h" +#import "HippyAssert.h" #import "HippyConvert.h" #import "HippyJSStackFrame.h" #import "HippyLog.h" @@ -458,7 +458,7 @@ + (void)setCurrentBridge:(nullable HippyBridge *)currentBridge { @end -#else // Disabled +#else /* HIPPY_DEBUG */ @implementation HippyRedBox @@ -492,6 +492,12 @@ - (HippyRedBox *)redBox { return nil; } ++ (nullable id)currentBridge { + return nil; +} + ++ (void)setCurrentBridge:(nullable HippyBridge *)currentBridge {} + @end -#endif +#endif /* HIPPY_DEBUG */ diff --git a/framework/ios/module/eventobserver/HippyEventObserverModule.mm b/framework/ios/module/eventobserver/HippyEventObserverModule.mm index 8c3404cfb83..1e97aa45468 100644 --- a/framework/ios/module/eventobserver/HippyEventObserverModule.mm +++ b/framework/ios/module/eventobserver/HippyEventObserverModule.mm @@ -22,7 +22,7 @@ #import "HippyEventObserverModule.h" #import "HippyEventDispatcher.h" -#import "HippyAsserts.h" +#import "HippyAssert.h" @implementation HippyEventObserverModule { NSMutableDictionary *_config; diff --git a/framework/ios/module/exception/HippyExceptionModule.mm b/framework/ios/module/exception/HippyExceptionModule.mm index fb90340fb08..d62c8c427ce 100644 --- a/framework/ios/module/exception/HippyExceptionModule.mm +++ b/framework/ios/module/exception/HippyExceptionModule.mm @@ -23,7 +23,7 @@ #import "HippyExceptionModule.h" #import "HippyBridge.h" #import "HippyDefines.h" -#import "HippyAsserts.h" +#import "HippyAssert.h" #import "HippyUtils.h" @implementation HippyExceptionModule diff --git a/framework/ios/module/localstorage/HippyAsyncLocalStorage.mm b/framework/ios/module/localstorage/HippyAsyncLocalStorage.mm index 660f441c9f9..ca5f3651ec5 100644 --- a/framework/ios/module/localstorage/HippyAsyncLocalStorage.mm +++ b/framework/ios/module/localstorage/HippyAsyncLocalStorage.mm @@ -26,7 +26,7 @@ #import "HippyBridge.h" #import "HippyDefines.h" #import "HippyUtils.h" -#import "HippyAsserts.h" +#import "HippyAssert.h" #import "HippyConvert.h" #import "HippyLog.h" #import "HippyUtils.h" diff --git a/framework/ios/module/netinfo/HippyNetInfo.mm b/framework/ios/module/netinfo/HippyNetInfo.mm index 61879703a3f..4d5c92e89c3 100644 --- a/framework/ios/module/netinfo/HippyNetInfo.mm +++ b/framework/ios/module/netinfo/HippyNetInfo.mm @@ -26,7 +26,7 @@ #import "HippyNetInfo.h" #import "HippyNetInfoIntenal.h" -#import "HippyAsserts.h" +#import "HippyAssert.h" @interface HippyNetInfo () { } diff --git a/framework/ios/module/netinfo/HippyNetInfoIntenal.m b/framework/ios/module/netinfo/HippyNetInfoIntenal.m index 25449ed0c7d..6d7b5e6c8ea 100644 --- a/framework/ios/module/netinfo/HippyNetInfoIntenal.m +++ b/framework/ios/module/netinfo/HippyNetInfoIntenal.m @@ -24,7 +24,7 @@ #import #import "HippyNetInfoIntenal.h" -#import "HippyAsserts.h" +#import "HippyAssert.h" #include diff --git a/framework/ios/module/network/HippyNetWork.mm b/framework/ios/module/network/HippyNetWork.mm index 6e8caa203a0..d10ffd8d02f 100644 --- a/framework/ios/module/network/HippyNetWork.mm +++ b/framework/ios/module/network/HippyNetWork.mm @@ -26,7 +26,7 @@ #import "HippyBridge+VFSLoader.h" #import "HippyDefines.h" #import "HippyNetWork.h" -#import "HippyAsserts.h" +#import "HippyAssert.h" #import "HippyUtils.h" static NSStringEncoding GetStringEncodingFromURLResponse(NSURLResponse *response) { diff --git a/framework/ios/module/turbo/HippyOCTurboModule.mm b/framework/ios/module/turbo/HippyOCTurboModule.mm index 9fe98209e93..840f7282316 100644 --- a/framework/ios/module/turbo/HippyOCTurboModule.mm +++ b/framework/ios/module/turbo/HippyOCTurboModule.mm @@ -25,7 +25,7 @@ #import "HippyOCTurboModule+Inner.h" #import "HippyTurboModuleManager.h" #import "HippyJSExecutor.h" -#import "HippyAsserts.h" +#import "HippyAssert.h" #import "HippyLog.h" #import "HippyUtils.h" #import "NSObject+CtxValue.h" diff --git a/framework/ios/module/turbo/HippyTurboModuleManager.mm b/framework/ios/module/turbo/HippyTurboModuleManager.mm index c5c5814f5d7..8fb172ae1fd 100644 --- a/framework/ios/module/turbo/HippyTurboModuleManager.mm +++ b/framework/ios/module/turbo/HippyTurboModuleManager.mm @@ -23,7 +23,7 @@ #import "HippyJSExecutor.h" #import "HippyModuleData.h" #import "HippyTurboModuleManager.h" -#import "HippyAsserts.h" +#import "HippyAssert.h" #include diff --git a/framework/ios/module/turbo/NSObject+HippyTurbo.m b/framework/ios/module/turbo/NSObject+HippyTurbo.m index f703373ef50..beb53714346 100644 --- a/framework/ios/module/turbo/NSObject+HippyTurbo.m +++ b/framework/ios/module/turbo/NSObject+HippyTurbo.m @@ -21,7 +21,7 @@ */ #import "HippyModuleMethod.h" -#import "HippyAsserts.h" +#import "HippyAssert.h" #import "HippyUtils.h" #import "NSObject+HippyTurbo.h" diff --git a/framework/ios/utils/NSObject+CtxValue.mm b/framework/ios/utils/NSObject+CtxValue.mm index c33a8fb3082..e196e04b8cf 100644 --- a/framework/ios/utils/NSObject+CtxValue.mm +++ b/framework/ios/utils/NSObject+CtxValue.mm @@ -21,8 +21,8 @@ */ #import "NSObject+CtxValue.h" -#import "HippyAsserts.h" - +#import "HippyAssert.h" +#import "HippyLog.h" #include "driver/napi/js_ctx.h" #include "driver/napi/js_ctx_value.h" #include "footstone/string_view.h" @@ -32,7 +32,7 @@ @implementation NSObject (CtxValue) - (CtxValuePtr)convertToCtxValue:(const CtxPtr &)context; { @autoreleasepool { - HippyAssert(NO, @"%@ must implemente convertToCtxValue method", NSStringFromClass([self class])); + HippyLogWarn(@"%@ must implemente convertToCtxValue method", NSStringFromClass([self class])); std::unordered_map valueMap; return context->CreateObject(valueMap); } diff --git a/framework/voltron/CHANGELOG.md b/framework/voltron/CHANGELOG.md index 93f6ee00e5e..d6663da7d66 100644 --- a/framework/voltron/CHANGELOG.md +++ b/framework/voltron/CHANGELOG.md @@ -1,3 +1,19 @@ +## 0.0.37 + +- resolve merge conflicts + +## 0.0.36 + +- update ffi_manager + +## 0.0.35 + +- update voltron_render to 0.0.25 to make flutter module faster + +## 0.0.34 + +- support voltron dynamic library load + ## 0.0.34 - update voltron_render to 0.0.24 to fix width calculate error and add modal navigator param diff --git a/framework/voltron/core/src/bridge/android/bridge_impl.cc b/framework/voltron/core/src/bridge/android/bridge_impl.cc index 88cc511d22c..061d17c0be4 100644 --- a/framework/voltron/core/src/bridge/android/bridge_impl.cc +++ b/framework/voltron/core/src/bridge/android/bridge_impl.cc @@ -243,7 +243,7 @@ void BridgeImpl::Destroy(int64_t scope_id, voltron::EraseObject(footstone::checked_numeric_cast< int64_t, uint32_t>(scope_id)); - JsDriverUtils::DestroyInstance(engine, scope, [callback](bool ret) { + JsDriverUtils::DestroyInstance(std::move(engine), std::move(scope), [callback](bool ret) { if (ret) { callback(INIT_CB_STATE::SUCCESS); } else { diff --git a/framework/voltron/example/android/build.gradle b/framework/voltron/example/android/build.gradle index 3aef9e69f0a..1162d4c4ba5 100644 --- a/framework/voltron/example/android/build.gradle +++ b/framework/voltron/example/android/build.gradle @@ -29,6 +29,6 @@ subprojects { project.evaluationDependsOn(':app') } -task clean(type: Delete) { +tasks.register("clean", Delete) { delete rootProject.buildDir } diff --git a/framework/voltron/example/android/gradle/wrapper/gradle-wrapper.properties b/framework/voltron/example/android/gradle/wrapper/gradle-wrapper.properties index b8793d3c0d6..6b665338b34 100644 --- a/framework/voltron/example/android/gradle/wrapper/gradle-wrapper.properties +++ b/framework/voltron/example/android/gradle/wrapper/gradle-wrapper.properties @@ -3,4 +3,4 @@ distributionBase=GRADLE_USER_HOME distributionPath=wrapper/dists zipStoreBase=GRADLE_USER_HOME zipStorePath=wrapper/dists -distributionUrl=https\://services.gradle.org/distributions/gradle-7.0.2-all.zip +distributionUrl=https\://services.gradle.org/distributions/gradle-7.5-all.zip diff --git a/framework/voltron/example/ios/Podfile.lock b/framework/voltron/example/ios/Podfile.lock index 53ddd961c89..0d8e223063d 100644 --- a/framework/voltron/example/ios/Podfile.lock +++ b/framework/voltron/example/ios/Podfile.lock @@ -21,11 +21,13 @@ PODS: - shared_preferences_foundation (0.0.1): - Flutter - FlutterMacOS - - sqflite (0.0.2): + - sqflite (0.0.3): - Flutter - FMDB (>= 2.7.5) - voltron (0.0.1): - Flutter + - voltron_screen_info (0.0.1): + - Flutter - webview_cookie_manager (0.0.1): - Flutter - webview_flutter_wkwebview (0.0.1): @@ -42,6 +44,7 @@ DEPENDENCIES: - shared_preferences_foundation (from `.symlinks/plugins/shared_preferences_foundation/ios`) - sqflite (from `.symlinks/plugins/sqflite/ios`) - voltron (from `.symlinks/plugins/voltron/ios`) + - voltron_screen_info (from `.symlinks/plugins/voltron_screen_info/ios`) - webview_cookie_manager (from `.symlinks/plugins/webview_cookie_manager/ios`) - webview_flutter_wkwebview (from `.symlinks/plugins/webview_flutter_wkwebview/ios`) @@ -71,6 +74,8 @@ EXTERNAL SOURCES: :path: ".symlinks/plugins/sqflite/ios" voltron: :path: ".symlinks/plugins/voltron/ios" + voltron_screen_info: + :path: ".symlinks/plugins/voltron_screen_info/ios" webview_cookie_manager: :path: ".symlinks/plugins/webview_cookie_manager/ios" webview_flutter_wkwebview: @@ -84,14 +89,15 @@ SPEC CHECKSUMS: gradient_like_css: 5bf90b38d326b3883145898d762735ca07d17991 keyboard_utils: ab24bc711be9e91a5937c20489056b8dd650fecc package_info_plus: fd030dabf36271f146f1f3beacd48f564b0f17f7 - path_provider_foundation: c68054786f1b4f3343858c1e1d0caaded73f0be9 + path_provider_foundation: eaf5b3e458fc0e5fbb9940fb09980e853fe058b8 ReachabilitySwift: 985039c6f7b23a1da463388634119492ff86c825 - shared_preferences_foundation: e2dae3258e06f44cc55f49d42024fd8dd03c590c - sqflite: 6d358c025f5b867b29ed92fc697fd34924e11904 + shared_preferences_foundation: 5b919d13b803cadd15ed2dc053125c68730e5126 + sqflite: 31f7eba61e3074736dff8807a9b41581e4f7f15a voltron: 573e6755e966a450ea768ab1074d237ad0f41d82 + voltron_screen_info: 3f0614af8cdf6aab0b174854a2c8a29fb58316d5 webview_cookie_manager: eaf920722b493bd0f7611b5484771ca53fed03f7 webview_flutter_wkwebview: 2e2d318f21a5e036e2c3f26171342e95908bd60a -PODFILE CHECKSUM: b4c93064582ae64fa0d6a91b4d9b0c383edd9a76 +PODFILE CHECKSUM: 9c6f6e76df8eb7d6e4cc1dbb5dae28135d6b4305 -COCOAPODS: 1.11.3 +COCOAPODS: 1.12.1 diff --git a/framework/voltron/example/lib/base_voltron_page.dart b/framework/voltron/example/lib/base_voltron_page.dart index aa2c087b5c1..06b69eac6b0 100644 --- a/framework/voltron/example/lib/base_voltron_page.dart +++ b/framework/voltron/example/lib/base_voltron_page.dart @@ -161,6 +161,8 @@ class _BaseVoltronPageState extends State { initParams.coreJSAssetsPath = _coreBundle; initParams.codeCacheTag = "common"; } + // 这里可以不传,默认就是flutterApp,如果是使用flutter module,建议这里使用flutterModule以获取更快的启动速度 + initParams.integratedMode = IntegratedMode.flutterApp; initParams.providers = [ MyAPIProvider(), ]; diff --git a/framework/voltron/example/lib/main.dart b/framework/voltron/example/lib/main.dart index 20c0f1d7df7..e33659c56e3 100644 --- a/framework/voltron/example/lib/main.dart +++ b/framework/voltron/example/lib/main.dart @@ -22,6 +22,8 @@ import 'dart:io'; import 'package:flutter/material.dart'; import 'package:flutter/services.dart'; +import 'package:path_provider/path_provider.dart'; +import 'package:voltron/voltron.dart'; import 'base_voltron_page.dart'; @@ -35,6 +37,7 @@ void main() { systemNavigationBarColor: Color(0XFFE5E5E5), )); } + runApp(MyApp()); } @@ -89,6 +92,18 @@ class MyHomePage extends StatefulWidget { } class _MyHomePageState extends State { + @override + void initState() { + super.initState(); + setLibrary(); + } + + void setLibrary() async { + // final path = (await getApplicationDocumentsDirectory()).path; + // 测试业务动态load library + // VoltronJSLoaderManager.initCustomLibrary(path: '$path/libs/arm64-v8a/', name: 'voltron_core'); + } + @override Widget build(BuildContext context) { return Scaffold( diff --git a/framework/voltron/lib/engine/engine_define.dart b/framework/voltron/lib/engine/engine_define.dart index ad8ab7c3d8f..90cace51270 100644 --- a/framework/voltron/lib/engine/engine_define.dart +++ b/framework/voltron/lib/engine/engine_define.dart @@ -47,6 +47,14 @@ enum EngineMode { singleThread, } +// IntegratedMode 集成模式 +// flutterApp 如果开发的是flutter app,推荐这种方式 +// flutterModule 如果开发的是native app + flutter module,推荐这种方式,这种模式下会通过method channel获取屏幕参数,更快 +enum IntegratedMode { + flutterApp, + flutterModule, +} + /// 引擎初始化过程中的错误码,对于Voltron sdk开发者调查Voltron sdk的使用者在使用过程中遇到的问题,很必须。 enum EngineInitStatus { ok, // 初始化过程,一切正常 diff --git a/framework/voltron/lib/engine/js_engine_context.dart b/framework/voltron/lib/engine/js_engine_context.dart index 6f23e979a3f..18dcca96c8e 100644 --- a/framework/voltron/lib/engine/js_engine_context.dart +++ b/framework/voltron/lib/engine/js_engine_context.dart @@ -93,6 +93,7 @@ class EngineContext with RenderContextProxy { VoltronBundleLoader? coreLoader, int bridgeType, bool isDevModule, + IntegratedMode integratedMode, String serverHost, int groupId, VoltronThirdPartyAdapter? thirdPartyAdapter, @@ -155,7 +156,8 @@ class EngineContext with RenderContextProxy { void _initVfsManager() { _vfsManager = VfsManager(); - DefaultProcessor processor = DefaultProcessor(VoltronResourceLoader(_globalConfigs.httpAdapter)); + DefaultProcessor processor = + DefaultProcessor(VoltronResourceLoader(_globalConfigs.httpAdapter)); _vfsManager.addProcessor(processor); } diff --git a/framework/voltron/lib/engine/js_init_params.dart b/framework/voltron/lib/engine/js_init_params.dart index bacb984ae07..44a6ccd531e 100644 --- a/framework/voltron/lib/engine/js_init_params.dart +++ b/framework/voltron/lib/engine/js_init_params.dart @@ -60,6 +60,9 @@ class EngineInitParams { // 可选参数 引擎模式 默认为NORMAL EngineMode engineMode = EngineMode.normal; + // 可选参数 + IntegratedMode integratedMode = IntegratedMode.flutterApp; + // 可选参数 自定义的,用来提供Native modules、JavaScript modules、View controllers的管理器。1个或多个 List? providers; diff --git a/framework/voltron/lib/engine/voltron_js_engine.dart b/framework/voltron/lib/engine/voltron_js_engine.dart index 14443b54795..df7ced918bc 100644 --- a/framework/voltron/lib/engine/voltron_js_engine.dart +++ b/framework/voltron/lib/engine/voltron_js_engine.dart @@ -66,6 +66,8 @@ class VoltronJSEngine implements OnResumeAndPauseListener, DevServerCallback { // 从网络上加载jsbundle late bool _debugMode; + late IntegratedMode _integratedMode; + // Server的jsbundle名字,调试模式下有效 late String _serverBundleName; @@ -125,17 +127,20 @@ class VoltronJSEngine implements OnResumeAndPauseListener, DevServerCallback { } try { + LogUtils.d(_kTag, "init ffi function binding start"); _initBridge(); + LogUtils.d(_kTag, "init ffi function binding done"); } catch (e) { _currentState = EngineState.initError; if (e is Error) { - LogUtils.e(_kTag, "${e.stackTrace}"); + LogUtils.e(_kTag, "init ffi function binding fail, error: ${e.stackTrace}"); } } - LogUtils.d(_kTag, "initEngine initBridge done"); _id = VoltronApi.getVoltronEngineIndex(); + LogUtils.d(_kTag, "get voltron engine index: ${_id}"); + CookieManager.getInstance().setCookieDelegate( params.cookieDelegateType, originDelegate: params.originDelegate, @@ -147,6 +152,7 @@ class VoltronJSEngine implements OnResumeAndPauseListener, DevServerCallback { _preloadBundleLoader = preloadBundleLoader; _apiProviders = params.providers; _debugMode = params.debugMode; + _integratedMode = params.integratedMode; _serverBundleName = params.debugMode ? params.debugBundleName : ""; _startTimeMonitor = TimeMonitor(true); _engineMonitor = params.engineMonitor!; @@ -158,17 +164,32 @@ class VoltronJSEngine implements OnResumeAndPauseListener, DevServerCallback { Future initEngine(EngineListener listener) async { _startTimeMonitor.startEvent(EngineMonitorEventKey.engineLoadEventInitEngine); + try { + // 初始化UI宽高信息, 必须放到第一位,否则可能run app之后没有UI界面宽高信息 + LogUtils.d(_kTag, "init screen info start"); + await ScreenUtil.getInstance().initScreen( + screenInfoSource: _integratedMode == IntegratedMode.flutterApp + ? ScreenInfoSource.flutter + : ScreenInfoSource.native); + LogUtils.d(_kTag, "init screen info done"); + } catch (e) { + _currentState = EngineState.initError; + if (e is Error) { + LogUtils.e(_kTag, "init screen info fail, error:${e.stackTrace}"); + } + } + try { // 初始化平台相关信息和UI宽高信息, 必须放到第一位,否则可能run app之后平台信息还未初始化完成,或者没有UI界面宽高信息 - await ScreenUtil.getInstance().ensurePhysicalSizeReady(); + LogUtils.d(_kTag, "init platform info start"); await PlatformManager.getInstance().initPlatform(); + LogUtils.d(_kTag, "init platform info done"); } catch (e) { _currentState = EngineState.initError; if (e is Error) { - LogUtils.e(_kTag, "${e.stackTrace}"); + LogUtils.e(_kTag, "init platform info fail, error:${e.stackTrace}"); } } - LogUtils.d(_kTag, "initEngine getPlatform done"); if (_currentState != EngineState.unInit) { _listen(listener); @@ -294,6 +315,7 @@ class VoltronJSEngine implements OnResumeAndPauseListener, DevServerCallback { _coreBundleLoader, bridgeType, _debugMode, + _integratedMode, _serverHost, _groupId, _thirdPartyAdapter, @@ -326,6 +348,7 @@ class VoltronJSEngine implements OnResumeAndPauseListener, DevServerCallback { var state = _currentState; _currentState = param ? EngineState.inited : EngineState.initError; if (state != EngineState.onRestart) { + LogUtils.d(_kTag, "restartEngineInBackground ok"); _notifyEngineInitialized(param ? EngineInitStatus.ok : EngineInitStatus.errBridge, e); } else { LogUtils.e(_kTag, "initBridge callback error STATUS_WRONG_STATE, state=$_currentState"); @@ -511,7 +534,6 @@ class VoltronJSEngine implements OnResumeAndPauseListener, DevServerCallback { static void _initBridge() { if (!_hasInit) { - LogUtils.d(_kTag, "_initBridge"); VoltronApi.initBridge(); _hasInit = true; } diff --git a/framework/voltron/lib/voltron/loader.dart b/framework/voltron/lib/voltron/loader.dart index c56bbc7c5c8..656fe3376f1 100644 --- a/framework/voltron/lib/voltron/loader.dart +++ b/framework/voltron/lib/voltron/loader.dart @@ -18,9 +18,7 @@ // limitations under the License. // -import 'package:voltron_renderer/voltron_renderer.dart'; - -import '../engine.dart'; +import 'package:voltron/voltron.dart'; typedef ModuleExecutor = Function(); @@ -34,6 +32,19 @@ class VoltronJSLoaderManager { _engine = VoltronJSEngine.create(params); } + /// 自定义so或者dylib库路径和名称,可用于动态加载library库 + /// 此方法需要在创建引擎前调用 + /// 注意,调用此方法需要自己判断当前机器的cpu架构,否则会导致加载失败 + /// 另外调用此方法需要保证路径和名称正确性,否则会导致加载失败 + static void initCustomLibrary({required String path, required String name}) { + if (path.isNotEmpty) { + FfiManager.libPath = path; + } + if (name.isNotEmpty) { + FfiManager.libraryName = name; + } + } + // 框架初始化 static VoltronJSLoaderManager createLoaderManager( EngineInitParams params, diff --git a/framework/voltron/pubspec.yaml b/framework/voltron/pubspec.yaml index 11f966cb37b..4a4629e7e11 100644 --- a/framework/voltron/pubspec.yaml +++ b/framework/voltron/pubspec.yaml @@ -22,7 +22,7 @@ name: voltron description: A flutter plugin project to render hippy page. -version: 0.0.34 +version: 0.0.37 homepage: https://hippyjs.org repository: https://github.com/Tencent/Hippy @@ -60,9 +60,9 @@ dependencies: connectivity_plus: ^3.0.6 uuid: ^3.0.6 ffi: ^2.0.0 - voltron_renderer: 0.0.24 - voltron_vfs: 0.0.6 - voltron_ffi: 0.0.5 + voltron_renderer: 0.0.27 + voltron_vfs: 0.0.7 + voltron_ffi: 0.0.6 dev_dependencies: flutter_test: diff --git a/hippy.podspec b/hippy.podspec index 38d6cdff13f..d3c97e215c8 100644 --- a/hippy.podspec +++ b/hippy.podspec @@ -129,6 +129,9 @@ Pod::Spec.new do |s| base.source_files = ['modules/ios/base/*.{h,m,mm}', 'modules/ios/logutils/*.{h,mm}'] base.public_header_files = ['modules/ios/base/*.h', 'modules/ios/logutils/*.h'] base.dependency 'hippy/Footstone' + base.pod_target_xcconfig = { + 'GCC_PREPROCESSOR_DEFINITIONS' => 'HIPPY_VERSION=' + s.version.to_s, + } puts 'hippy subspec \'base\' read end' end diff --git a/modules/footstone/include/footstone/logging.h b/modules/footstone/include/footstone/logging.h index 9687cde3adf..f4694117ace 100644 --- a/modules/footstone/include/footstone/logging.h +++ b/modules/footstone/include/footstone/logging.h @@ -119,7 +119,8 @@ class LogMessage { } std::ostringstream s; - s<<"thread:"< *functionStack = threadDictionary[HippyLogFunctionStack]; + HippyLogFunction logFunction = functionStack.lastObject; + if (logFunction) { + return logFunction; + } + return HippyGetLogFunction(); +} + void HippyPerformBlockWithLogFunction(void (^block)(void), HippyLogFunction logFunction) { NSMutableDictionary *threadDictionary = [NSThread currentThread].threadDictionary; NSMutableArray *functionStack = threadDictionary[HippyLogFunctionStack]; @@ -96,7 +117,7 @@ void HippyPerformBlockWithLogFunction(void (^block)(void), HippyLogFunction logF } void HippyPerformBlockWithLogPrefix(void (^block)(void), NSString *prefix) { - HippyLogFunction logFunction = HippyGetLogFunction(); + HippyLogFunction logFunction = HippyGetLocalLogFunction(); if (logFunction) { HippyPerformBlockWithLogFunction(block, ^(HippyLogLevel level, HippyLogSource source, @@ -153,7 +174,7 @@ void HippyPerformBlockWithLogPrefix(void (^block)(void), NSString *prefix) { } void HippyLogNativeInternal(HippyLogLevel level, const char *fileName, int lineNumber, NSString *format, ...) { - HippyLogFunction logFunction = HippyGetLogFunction(); + HippyLogFunction logFunction = HippyGetLocalLogFunction(); BOOL log = HIPPY_DEBUG || (logFunction != nil); if (log && level >= HippyGetLogThreshold()) { // Get message diff --git a/modules/ios/base/HippyUtils.h b/modules/ios/base/HippyUtils.h index a1038c20b46..7267de0c790 100644 --- a/modules/ios/base/HippyUtils.h +++ b/modules/ios/base/HippyUtils.h @@ -142,4 +142,19 @@ HIPPY_EXTERN NSString *HippySchemeFromURLString(NSString *urlString); //Get String Encoding From HTTP URL Response HIPPY_EXTERN NSStringEncoding HippyGetStringEncodingFromURLResponse(NSURLResponse *response); + +#pragma mark - + +/// 工具类 +/// 注意,类名及方法名禁止修改 +@interface HippyUtils : NSObject + +/// HippySDK版本号 +/// +/// 注意方法名禁止修改,外部可能会动态调用,判断Hippy版本号 ++ (NSString *)sdkVersion; + +@end + + NS_ASSUME_NONNULL_END diff --git a/modules/ios/base/HippyUtils.m b/modules/ios/base/HippyUtils.m index aa987ed49ad..e257b5a0cc7 100644 --- a/modules/ios/base/HippyUtils.m +++ b/modules/ios/base/HippyUtils.m @@ -21,7 +21,7 @@ */ #import "HippyUtils.h" -#import "HippyAsserts.h" +#import "HippyAssert.h" #import "HippyLog.h" #import #import @@ -698,3 +698,14 @@ id HippyJSONClean(id object) { return HippyMakeError(errorMessage, nil, errorInfo); } + + +#pragma mark - + +@implementation HippyUtils + ++ (NSString *)sdkVersion { + return @(HIPPY_STR(HIPPY_VERSION)); +} + +@end diff --git a/modules/vfs/voltron/CHANGELOG.md b/modules/vfs/voltron/CHANGELOG.md index a44510908f2..e625d662bd4 100644 --- a/modules/vfs/voltron/CHANGELOG.md +++ b/modules/vfs/voltron/CHANGELOG.md @@ -1,3 +1,7 @@ +## 0.0.7 + +- update ffi_manager to 0.0.6 + ## 0.0.6 - fix build error diff --git a/modules/vfs/voltron/pubspec.yaml b/modules/vfs/voltron/pubspec.yaml index 665eebe24f4..2a131787bcb 100644 --- a/modules/vfs/voltron/pubspec.yaml +++ b/modules/vfs/voltron/pubspec.yaml @@ -22,7 +22,7 @@ name: voltron_vfs description: voltron vfs module -version: 0.0.6 +version: 0.0.7 homepage: https://hippyjs.org repository: https://github.com/Tencent/Hippy @@ -34,4 +34,4 @@ dependencies: flutter: sdk: flutter ffi: ^2.0.0 - voltron_ffi: 0.0.5 + voltron_ffi: 0.0.6 diff --git a/modules/voltron/ffi/CHANGELOG.md b/modules/voltron/ffi/CHANGELOG.md index 50bdbc70f26..744c3805ae0 100644 --- a/modules/voltron/ffi/CHANGELOG.md +++ b/modules/voltron/ffi/CHANGELOG.md @@ -1,3 +1,7 @@ +## 0.0.6 + +- support dynamic library lib + ## 0.0.5 - update ffi to 2.0.0 diff --git a/modules/voltron/ffi/lib/ffi_manager.dart b/modules/voltron/ffi/lib/ffi_manager.dart index d73751bea8d..d8d244475dc 100644 --- a/modules/voltron/ffi/lib/ffi_manager.dart +++ b/modules/voltron/ffi/lib/ffi_manager.dart @@ -30,6 +30,8 @@ import 'global_callback.dart'; enum _DefaultRegisterFuncType { globalCallback } class FfiManager { + static String libPath = ''; + static String libraryName = 'voltron_core'; static const String _kDefaultRegisterHeader = 'default'; final _interactiveCppRequests = ReceivePort()..listen(requestExecuteCallback); @@ -49,7 +51,7 @@ class FfiManager { String get registerFuncName => 'AddCallFunc'; - final DynamicLibrary _library = loadLibrary('voltron_core', isStatic: false); + final DynamicLibrary _library = loadLibrary(libraryName, isStatic: false, path: libPath); FfiManager._internal() { _initFfi = _library.lookupFunction< diff --git a/modules/voltron/ffi/pubspec.yaml b/modules/voltron/ffi/pubspec.yaml index 19f943003a2..beae5cf78b2 100644 --- a/modules/voltron/ffi/pubspec.yaml +++ b/modules/voltron/ffi/pubspec.yaml @@ -22,7 +22,7 @@ name: voltron_ffi description: voltron ffi module -version: 0.0.5 +version: 0.0.6 homepage: https://hippyjs.org repository: https://github.com/Tencent/Hippy diff --git a/renderer/native/android/src/main/java/com/tencent/mtt/hippy/uimanager/ControllerUpdateManger.java b/renderer/native/android/src/main/java/com/tencent/mtt/hippy/uimanager/ControllerUpdateManger.java index 2865c3862e9..954dfc1c328 100644 --- a/renderer/native/android/src/main/java/com/tencent/mtt/hippy/uimanager/ControllerUpdateManger.java +++ b/renderer/native/android/src/main/java/com/tencent/mtt/hippy/uimanager/ControllerUpdateManger.java @@ -190,13 +190,8 @@ private void invokePropMethod(@NonNull Object obj, @NonNull Object arg1, } } - private void handleCustomProps(T t, @Nullable View g, @NonNull String key, - @NonNull Map props) { - if (g == null) { - return; - } - boolean hasCustomMethodHolder = false; - Object customProps = props.get(key); + @Nullable + private PropertyMethodHolder getCustomPropsMethodHolder(@NonNull String key) { if (mCustomPropsController != null && mCustomPropsController instanceof HippyCustomPropsController) { Class cls = mCustomPropsController.getClass(); @@ -205,16 +200,22 @@ private void handleCustomProps(T t, @Nullable View g, @NonNull String key, methodHolderMap = new HashMap<>(); findViewPropsMethod(cls, methodHolderMap); } - PropertyMethodHolder methodHolder = methodHolderMap.get(key); + return methodHolderMap.get(key); + } + return null; + } + + private void handleCustomProps(T t, @Nullable View view, @NonNull String key, + @NonNull Map props, @Nullable PropertyMethodHolder methodHolder) { + if (view != null) { + Object customProps = props.get(key); if (methodHolder != null) { - invokePropMethod(mCustomPropsController, g, props, key, methodHolder); - hasCustomMethodHolder = true; + invokePropMethod(mCustomPropsController, view, props, key, methodHolder); + } else if (t instanceof HippyViewController) { + //noinspection unchecked + ((HippyViewController) t).setCustomProp(view, key, customProps); } } - if (!hasCustomMethodHolder && t instanceof HippyViewController) { - //noinspection unchecked - ((HippyViewController) t).setCustomProp(g, key, customProps); - } } protected void updateProps(@NonNull RenderNode node, @NonNull T controller, @Nullable View view, @@ -254,7 +255,14 @@ protected void updateProps(@NonNull RenderNode node, @NonNull T controller, @Nul MapUtils.getIntValue(props, NodeProps.BACKGROUND_COLOR, Color.TRANSPARENT)); } else if (!handleComponentProps(node, key, props, skipComponentProps)) { - handleCustomProps(controller, view, key, props); + PropertyMethodHolder customMethodHolder = getCustomPropsMethodHolder(key); + if (customMethodHolder != null && view == null) { + // If the host has a custom attribute that needs to be processed, this element cannot be + // flattened, otherwise, if the view is empty, custom attributes will not be passed through + // to custom props controller. + view = node.createView(true); + } + handleCustomProps(controller, view, key, props, customMethodHolder); } } } diff --git a/renderer/native/android/src/main/java/com/tencent/mtt/hippy/views/hippylist/HippyRecyclerViewController.java b/renderer/native/android/src/main/java/com/tencent/mtt/hippy/views/hippylist/HippyRecyclerViewController.java index 7ffa109ef92..5005a3dfecb 100644 --- a/renderer/native/android/src/main/java/com/tencent/mtt/hippy/views/hippylist/HippyRecyclerViewController.java +++ b/renderer/native/android/src/main/java/com/tencent/mtt/hippy/views/hippylist/HippyRecyclerViewController.java @@ -223,18 +223,14 @@ public void setSuspendViewListener(final HRW viewWrapper, int open) { @HippyControllerProps(name = "overScrollEnabled", defaultType = HippyControllerProps.BOOLEAN, defaultBoolean = false) public void setOverScrollEnable(HRW viewWrapper, boolean flag) { - if (flag) { - viewWrapper.setOverScrollMode(View.OVER_SCROLL_ALWAYS); - } else { - viewWrapper.setOverScrollMode(View.OVER_SCROLL_NEVER); - } setBounces(viewWrapper, flag); } @HippyControllerProps(name = OVER_PULL, defaultType = HippyControllerProps.BOOLEAN, defaultBoolean = true) public void setBounces(HRW viewWrapper, boolean flag) { - HippyRecyclerView recyclerView = viewWrapper.getRecyclerView(); + HippyRecyclerView recyclerView = viewWrapper.getRecyclerView(); if (recyclerView != null) { + recyclerView.setOverScrollMode(flag ? View.OVER_SCROLL_ALWAYS : View.OVER_SCROLL_NEVER); recyclerView.setEnableOverPull(flag); } } diff --git a/renderer/native/android/src/main/java/com/tencent/mtt/hippy/views/hippylist/RecyclerViewEventHelper.java b/renderer/native/android/src/main/java/com/tencent/mtt/hippy/views/hippylist/RecyclerViewEventHelper.java index adfc0b2db5c..849471f6a1f 100644 --- a/renderer/native/android/src/main/java/com/tencent/mtt/hippy/views/hippylist/RecyclerViewEventHelper.java +++ b/renderer/native/android/src/main/java/com/tencent/mtt/hippy/views/hippylist/RecyclerViewEventHelper.java @@ -88,7 +88,7 @@ public boolean onPreDraw() { }; } - void notifyInitialListReady() { + protected void notifyInitialListReady() { if (canNotifyInit()) { isInitialListReadyNotified = true; viewTreeObserver.removeOnPreDrawListener(preDrawListener); diff --git a/renderer/native/ios/renderer/HippyRootView.mm b/renderer/native/ios/renderer/HippyRootView.mm index e27f02c2b0d..c2d308993c2 100644 --- a/renderer/native/ios/renderer/HippyRootView.mm +++ b/renderer/native/ios/renderer/HippyRootView.mm @@ -21,7 +21,7 @@ */ #import "HippyRootView.h" -#import "HippyAsserts.h" +#import "HippyAssert.h" #import "HippyView.h" #import "UIView+Hippy.h" #import "NativeRenderDefines.h" @@ -29,6 +29,7 @@ #import "HippyBridge.h" #import "HippyUIManager.h" #import "HippyDeviceBaseInfo.h" +#import "HippyTouchHandler.h" #include // Sent when the first subviews are added to the root view @@ -51,7 +52,7 @@ @interface HippyRootContentView : HippyView @property (nonatomic, readonly) BOOL contentHasAppeared; -//@property (nonatomic, strong) HippyTouchHandler *touchHandler; +@property (nonatomic, strong) HippyTouchHandler *touchHandler; @property (nonatomic, assign) int64_t startTimpStamp; - (instancetype)initWithFrame:(CGRect)frame @@ -200,7 +201,7 @@ - (BOOL)canBecomeFirstResponder { } - (void)cancelTouches { - // [[_contentView touchHandler] cancelTouch]; + [[_contentView touchHandler] cancelTouch]; } - (NSNumber *)hippyTag { @@ -293,9 +294,9 @@ - (instancetype)initWithFrame:(CGRect)frame _bridge = bridge; self.hippyTag = hippyTag; - // FIXME: HippyTouchHandler - // _touchHandler = [[HippyTouchHandler alloc] initWithRootView:self bridge:bridge]; - // [self addGestureRecognizer:_touchHandler]; + // 添加Hippy自定义手势识别器,用于管理手势事件,并将其发送至js端。 + _touchHandler = [[HippyTouchHandler alloc] initWithRootView:self bridge:bridge]; + [self addGestureRecognizer:_touchHandler]; self.layer.backgroundColor = NULL; _startTimpStamp = CACurrentMediaTime() * 1000; @@ -320,6 +321,13 @@ - (void)setFrame:(CGRect)frame { } } +- (void)hippySetFrame:(CGRect)frame { + // Override hippySetFrame, + // to prevent an endless loop when synchronizing shadowView's frame to view. + // HippyRootContentView's frame is the source of truth, + // So we do nothing here. +} + #pragma mark - HippyComponent Method - (void)insertHippySubview:(UIView *)subview atIndex:(NSInteger)atIndex { diff --git a/renderer/native/ios/renderer/NativeRenderScrollProtocol.h b/renderer/native/ios/renderer/HippyScrollProtocol.h similarity index 94% rename from renderer/native/ios/renderer/NativeRenderScrollProtocol.h rename to renderer/native/ios/renderer/HippyScrollProtocol.h index 07294c07581..68a1ae93b85 100644 --- a/renderer/native/ios/renderer/NativeRenderScrollProtocol.h +++ b/renderer/native/ios/renderer/HippyScrollProtocol.h @@ -22,7 +22,7 @@ #import -@protocol NativeRenderScrollProtocol +@protocol HippyScrollProtocol @required diff --git a/renderer/native/ios/renderer/HippyUIManager+Private.h b/renderer/native/ios/renderer/HippyUIManager+Private.h new file mode 100644 index 00000000000..67bfeff24b3 --- /dev/null +++ b/renderer/native/ios/renderer/HippyUIManager+Private.h @@ -0,0 +1,164 @@ +/*! + * iOS SDK + * + * Tencent is pleased to support the open source community by making + * Hippy available. + * + * Copyright (C) 2019 THL A29 Limited, a Tencent company. + * All rights reserved. + * + * Licensed 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 HippyUIManager_Private_h +#define HippyUIManager_Private_h + +#include +#include +#include + +class VFSUriLoader; +namespace hippy { +inline namespace dom { +class RenderManager; +class DomManager; +class DomArgument; +class RootNode; +class DomNode; +struct LayoutResult; +using CallFunctionCallback = std::function)>; +} +} + +namespace footstone { +inline namespace value { +class HippyValue; +} +} + +@interface HippyUIManager (Private) + +- (void)registerRootView:(UIView *)rootView asRootNode:(std::weak_ptr)rootNode; + +- (void)unregisterRootViewFromTag:(NSNumber *)rootTag; + + +/** + * create views from dom nodes + * @param nodes A set of nodes for creating views + */ +- (void)createRenderNodes:(std::vector> &&)nodes + onRootNode:(std::weak_ptr)rootNode; + +/** + * update views' properties from dom nodes + * @param nodes A set of nodes for updating views' properties + */ +- (void)updateRenderNodes:(std::vector> &&)nodes + onRootNode:(std::weak_ptr)rootNode; + +/** + * delete views from dom nodes + * @param nodes nodes to delete + */ +- (void)deleteRenderNodesIds:(std::vector> &&)nodes + onRootNode:(std::weak_ptr)rootNode; + +/** + * move views from container to another container + * + * @param ids A set of nodes id to move + * @param fromContainer Source view container from which views move + * @param toContainer Target view container to which views move + */ +- (void)renderMoveViews:(const std::vector &&)ids + fromContainer:(int32_t)fromContainer + toContainer:(int32_t)toContainer + index:(int32_t)index + onRootNode:(std::weak_ptr)rootNode; + +- (void)renderMoveNodes:(std::vector> &&)nodes + onRootNode:(std::weak_ptr)rootNode; +/** + * update layout for view + * + * @param layoutInfos Vector for nodes layout infos + */ +- (void)updateNodesLayout:(const std::vector> &)layoutInfos + onRootNode:(std::weak_ptr)rootNode; + +/** + * call function of view + * + * @param functionName Function Name to be invoked + * @param viewName Name of target view whose function invokes + * @param hippyTag id of target view whose function invokes + * @param params parameters of function to be invoked + * @param cb A callback for the return value of function + * + * @result Function return result + */ +- (id)dispatchFunction:(const std::string &)functionName + viewName:(const std::string &)viewName + viewTag:(int32_t)hippyTag + onRootNode:(std::weak_ptr)rootNode + params:(const footstone::value::HippyValue &)params + callback:(hippy::CallFunctionCallback)cb; + + +/** + * set dom manager for NativeRenderUIManager which holds a weak reference to domManager + */ +- (void)setDomManager:(std::weak_ptr)domManager; + +/** + * Invoked after batched operations completed + */ +- (void)batchOnRootNode:(std::weak_ptr)rootNode; + +/** + * register event for specific view + * + * @param name event name + * @param node_id id for node for the event + */ +- (void)addEventName:(const std::string &)name + forDomNodeId:(int32_t)node_id + onRootNode:(std::weak_ptr)rootNode; + +/** + * unregister event for specific view + * + * @param name event name + * @param node_id node id for the event + */ +- (void)removeEventName:(const std::string &)name + forDomNodeId:(int32_t)node_id + onRootNode:(std::weak_ptr)rootNode; + +/** + * unregister vsync event + */ +- (void)removeVSyncEventOnRootNode:(std::weak_ptr)rootNode; + +/** + * Set root view size changed event callback + * + *@param cb callback + */ +- (void)setRootViewSizeChangedEvent:(std::function)cb; + + +@end + +#endif /* HippyUIManager_Private_h */ diff --git a/renderer/native/ios/renderer/HippyUIManager.h b/renderer/native/ios/renderer/HippyUIManager.h index 1fa540a5a14..6c26f084c86 100644 --- a/renderer/native/ios/renderer/HippyUIManager.h +++ b/renderer/native/ios/renderer/HippyUIManager.h @@ -25,105 +25,80 @@ #import "HippyInvalidating.h" #import "NativeRenderDefines.h" #import "HippyBridgeModule.h" -#include -#include -#include - -@class NativeRenderAnimationViewParams, HippyShadowView, HippyUIManager,HippyViewManager; -@class NativeRenderReusePool, HippyComponentMap; - -class VFSUriLoader; -namespace hippy { -inline namespace dom { -class RenderManager; -class DomManager; -class DomArgument; -class RootNode; -class DomNode; -struct LayoutResult; -using CallFunctionCallback = std::function)>; -} -} - -namespace footstone { -inline namespace value { -class HippyValue; -} -} - +#import "HippyCustomTouchHandlerProtocol.h" + +@class NativeRenderAnimationViewParams; +@class HippyShadowView; +@class HippyUIManager; +@class HippyViewManager; +@class NativeRenderReusePool; +@class HippyComponentMap; @protocol HippyImageProviderProtocol; -/** - * The NativeRenderUIManager is the module responsible for updating the view hierarchy. - */ + +/// The HippyUIManager responsible for updating the view hierarchy. @interface HippyUIManager : NSObject ++ (instancetype)new NS_UNAVAILABLE; - (instancetype)init NS_UNAVAILABLE; +#ifdef __cplusplus +/// Init method +/// - Parameter renderManager: the hippy::RenderManager - (instancetype)initWithRenderManager:(std::weak_ptr)renderManager NS_DESIGNATED_INITIALIZER; -@property(nonatomic, assign) BOOL uiCreationLazilyEnabled; - -@property (nonatomic, weak) HippyBridge *bridge; @property (nonatomic, assign) std::weak_ptr VFSUriLoader; @property (nonatomic, assign) std::weak_ptr renderManager; @property (nonatomic, readonly) std::weak_ptr domManager; -@property (nonatomic, readonly) HippyComponentMap *viewRegistry; - -/** - * Gets the view associated with a hippyTag. - */ -- (UIView *)viewForComponentTag:(NSNumber *)componentTag - onRootTag:(NSNumber *)rootTag; - - -/** - * Get the shadow view associated with a hippyTag - */ -- (HippyShadowView *)renderObjectForcomponentTag:(NSNumber *)componentTag - onRootTag:(NSNumber *)rootTag; +#endif -/** - * Update the frame of a view. This might be in response to a screen rotation - * or some other layout event outside of the Hippy-managed view hierarchy. - */ +@property (nonatomic, weak) HippyBridge *bridge; +@property (nonatomic, readonly) HippyComponentMap *viewRegistry; +@property (nonatomic, assign) BOOL uiCreationLazilyEnabled; + + +/// Gets the view associated with a hippyTag. +/// - Parameters: +/// - hippyTag: NSNumber +/// - rootTag: NSNumber +- (UIView *)viewForHippyTag:(NSNumber *)hippyTag onRootTag:(NSNumber *)rootTag; + +/// Get the shadow view associated with a hippyTag +/// - Parameters: +/// - hippyTag: NSNumber +/// - rootTag: NSNumber +- (HippyShadowView *)shadowViewForHippyTag:(NSNumber *)hippyTag onRootTag:(NSNumber *)rootTag; + +/// Update the frame of a view. This might be in response to a screen rotation +/// or some other layout event outside of the Hippy-managed view hierarchy. +/// - Parameters: +/// - frame: new frame +/// - view: target view - (void)setFrame:(CGRect)frame forRootView:(UIView *)view; -/** - * Schedule a block to be executed on the UI thread. Useful if you need to execute - * view logic after all currently queued view updates have completed. - */ +/// Schedule a block to be executed on the UI thread. Useful if you need to execute +/// view logic after all currently queued view updates have completed. +/// - Parameter block: block to be executed on main thread - (void)addUIBlock:(HippyViewManagerUIBlock)block; -/** - * In some cases we might want to trigger layout from native side. - * Hippy won't be aware of this, so we need to make sure it happens. - */ +/// In some cases we might want to trigger layout from native side. +/// Hippy won't be aware of this, so we need to make sure it happens. +/// - Parameter tag: root tag - (void)setNeedsLayoutForRootNodeTag:(NSNumber *)tag; -- (void)registerRootView:(UIView *)rootView asRootNode:(std::weak_ptr)rootNode; - -- (void)unregisterRootViewFromTag:(NSNumber *)rootTag; - +/// Get all rootView - (NSArray<__kindof UIView *> *)rootViews; -- (__kindof UIView *)viewFromRenderViewTag:(NSNumber *)componentTag onRootTag:(NSNumber *)rootTag; - +/// Purge view from superView - (void)purgeViewsFromComponentTags:(NSArray *)componentTag onRootTag:(NSNumber *)rootTag; +/// Update view with props - (void)updateView:(NSNumber *)componentTag onRootTag:(NSNumber *)rootTag props:(NSDictionary *)pros; +/// Get viewManager Name +/// @param viewName NSString - (__kindof HippyViewManager *)renderViewManagerForViewName:(NSString *)viewName; -/** - * Manully create views recursively from hippy tag - * - * @param hippyTag hippy tag corresponding to UIView - * @return view created by hippy tag - */ -- (UIView *)createViewRecursivelyFromcomponentTag:(NSNumber *)hippyTag - onRootTag:(NSNumber *)rootTag; - /** * Manully create views recursively from renderObject * @@ -132,126 +107,18 @@ class HippyValue; */ - (UIView *)createViewRecursivelyFromRenderObject:(HippyShadowView *)renderObject; -/** - * set dom manager for NativeRenderUIManager which holds a weak reference to domManager - */ -- (void)setDomManager:(std::weak_ptr)domManager; - -/** - * create views from dom nodes - * @param nodes A set of nodes for creating views - */ -- (void)createRenderNodes:(std::vector> &&)nodes - onRootNode:(std::weak_ptr)rootNode; - -/** - * update views' properties from dom nodes - * @param nodes A set of nodes for updating views' properties - */ -- (void)updateRenderNodes:(std::vector> &&)nodes - onRootNode:(std::weak_ptr)rootNode; - -/** - * delete views from dom nodes - * @param nodes nodes to delete - */ -- (void)deleteRenderNodesIds:(std::vector> &&)nodes - onRootNode:(std::weak_ptr)rootNode; - -/** - * move views from container to another container - * - * @param ids A set of nodes id to move - * @param fromContainer Source view container from which views move - * @param toContainer Target view container to which views move - */ -- (void)renderMoveViews:(const std::vector &&)ids - fromContainer:(int32_t)fromContainer - toContainer:(int32_t)toContainer - index:(int32_t)index - onRootNode:(std::weak_ptr)rootNode; - -- (void)renderMoveNodes:(std::vector> &&)nodes - onRootNode:(std::weak_ptr)rootNode; -/** - * update layout for view - * - * @param layoutInfos Vector for nodes layout infos - */ -- (void)updateNodesLayout:(const std::vector> &)layoutInfos - onRootNode:(std::weak_ptr)rootNode; - -/** - * Invoked after batched operations completed - */ -- (void)batchOnRootNode:(std::weak_ptr)rootNode; - -/** - * call function of view - * - * @param functionName Function Name to be invoked - * @param viewName Name of target view whose function invokes - * @param hippyTag id of target view whose function invokes - * @param params parameters of function to be invoked - * @param cb A callback for the return value of function - * - * @result Function return result - */ -- (id)dispatchFunction:(const std::string &)functionName - viewName:(const std::string &)viewName - viewTag:(int32_t)hippyTag - onRootNode:(std::weak_ptr)rootNode - params:(const footstone::value::HippyValue &)params - callback:(hippy::CallFunctionCallback)cb; - +/// Register extra components +/// @param extraComponents extra components classes - (void)registerExtraComponent:(NSArray *)extraComponents; -/** - * register event for specific view - * - * @param name event name - * @param node_id id for node for the event - */ -- (void)addEventName:(const std::string &)name - forDomNodeId:(int32_t)node_id - onRootNode:(std::weak_ptr)rootNode; - -/** - * unregister event for specific view - * - * @param name event name - * @param node_id node id for the event - */ -- (void)removeEventName:(const std::string &)name - forDomNodeId:(int32_t)node_id - onRootNode:(std::weak_ptr)rootNode; - -/** - * unregister vsync event - */ -- (void)removeVSyncEventOnRootNode:(std::weak_ptr)rootNode; - -/** - * Set root view size changed event callback - * - *@param cb callback - */ -- (void)setRootViewSizeChangedEvent:(std::function)cb; - -/** - * clear all resources - */ +/// Clear all resources - (void)invalidate; -#if HIPPY_DEBUG -@property(nonatomic, assign) std::unordered_map>> domNodesMap; -- (std::shared_ptr)domNodeForTag:(int32_t)dom_tag onRootNode:(int32_t)root_tag; -- (std::vector>)childrenForNodeTag:(int32_t)tag onRootNode:(int32_t)root_tag; -#endif - @end +#pragma mark - HippyBridge (HippyUIManager) + /** * This category makes the current HippyUIManager instance available via the * HippyBridge, which is useful for HippyBridgeModules or HippyViewManagers that @@ -262,4 +129,8 @@ class HippyValue; /// The current HippyUIManager instance @property (nonatomic, readonly) HippyUIManager *uiManager; +/// A custom touch handler for gesture special processing +/// You can use it when you need to modify Hippy's default gesture handling logic +@property (nonatomic, strong, readonly) id customTouchHandler; + @end diff --git a/renderer/native/ios/renderer/HippyUIManager.mm b/renderer/native/ios/renderer/HippyUIManager.mm index 9a0007b820b..5c2da165fe9 100644 --- a/renderer/native/ios/renderer/HippyUIManager.mm +++ b/renderer/native/ios/renderer/HippyUIManager.mm @@ -20,7 +20,7 @@ * limitations under the License. */ -#import "HippyAsserts.h" +#import "HippyAssert.h" #import "HippyDomUtils.h" #import "HippyFootstoneUtils.h" #import "HippyOCToDomArgument.h" @@ -31,6 +31,7 @@ #import "HippyComponentData.h" #import "HippyComponentMap.h" #import "HippyUIManager.h" +#import "HippyUIManager+Private.h" #import "NativeRenderObjectRootView.h" #import "HippyShadowView.h" #import "NativeRenderUtils.h" @@ -46,6 +47,7 @@ #import "NativeRenderManager.h" #include "dom/root_node.h" #include "objc/runtime.h" +#include using HippyValue = footstone::value::HippyValue; using DomArgument = hippy::dom::DomArgument; @@ -184,6 +186,12 @@ @interface HippyUIManager() { std::function _rootViewSizeChangedCb; } +#if HIPPY_DEBUG +@property(nonatomic, assign) std::unordered_map>> domNodesMap; +- (std::shared_ptr)domNodeForTag:(int32_t)dom_tag onRootNode:(int32_t)root_tag; +- (std::vector>)childrenForNodeTag:(int32_t)tag onRootNode:(int32_t)root_tag; +#endif + @end @implementation HippyUIManager @@ -270,18 +278,17 @@ - (HippyComponentMap *)viewRegistry { - (__kindof UIView *)viewFromRenderViewTag:(NSNumber *)componentTag onRootTag:(NSNumber *)rootTag { - return [self viewForComponentTag:componentTag onRootTag:rootTag]; + return [self viewForHippyTag:componentTag onRootTag:rootTag]; } -- (UIView *)viewForComponentTag:(NSNumber *)componentTag - onRootTag:(NSNumber *)rootTag { +- (UIView *)viewForHippyTag:(NSNumber *)hippyTag onRootTag:(NSNumber *)rootTag { AssertMainQueue(); - return [_viewRegistry componentForTag:componentTag onRootTag:rootTag]; + return [_viewRegistry componentForTag:hippyTag onRootTag:rootTag]; } -- (HippyShadowView *)renderObjectForcomponentTag:(NSNumber *)componentTag +- (HippyShadowView *)shadowViewForHippyTag:(NSNumber *)hippyTag onRootTag:(NSNumber *)rootTag { - return [_shadowViewRegistry componentForTag:componentTag onRootTag:rootTag]; + return [_shadowViewRegistry componentForTag:hippyTag onRootTag:rootTag]; } - (std::weak_ptr)renderManager { @@ -444,7 +451,7 @@ - (void)purgeChildren:(NSArray> *)children - (void)purgeViewsFromComponentTags:(NSArray *)componentTags onRootTag:(NSNumber *)rootTag { for (NSNumber *componentTag in componentTags) { - UIView *view = [self viewForComponentTag:componentTag onRootTag:rootTag]; + UIView *view = [self viewForHippyTag:componentTag onRootTag:rootTag]; HippyComponentMap *componentMap = _viewRegistry; NativeRenderTraverseViewNodes(view, ^(id subview) { NSAssert(![subview isHippyRootView], @"Root views should not be unregistered"); @@ -459,12 +466,6 @@ - (void)removeChildren:(NSArray> *)children fromContainer:(id } } -- (UIView *)createViewRecursivelyFromcomponentTag:(NSNumber *)componentTag - onRootTag:(NSNumber *)rootTag { - HippyShadowView *renderObject = [_shadowViewRegistry componentForTag:componentTag onRootTag:rootTag]; - return [self createViewRecursivelyFromRenderObject:renderObject]; -} - - (UIView *)createViewFromRenderObject:(HippyShadowView *)renderObject { AssertMainQueue(); HippyAssert(renderObject.viewName, @"view name is needed for creating a view"); @@ -555,7 +556,7 @@ - (UIView *)createViewByComponentData:(HippyComponentData *)componentData rootTag:(NSNumber *)rootTag properties:(NSDictionary *)props viewName:(NSString *)viewName { - UIView *view = [self viewForComponentTag:componentTag onRootTag:rootTag]; + UIView *view = [self viewForHippyTag:componentTag onRootTag:rootTag]; BOOL canBeRetrievedFromCache = YES; if (view && [view respondsToSelector:@selector(canBeRetrievedFromViewCache)]) { canBeRetrievedFromCache = [view canBeRetrievedFromViewCache]; @@ -677,8 +678,8 @@ - (void)flushUIBlocksOnRootNode:(std::weak_ptr)rootNode { if (!strongRootNode) { return; } - unsigned int rand = arc4random(); - HP_PERF_LOG("flushUIBlocksOnRootNode(random id:%u", rand); + + TDF_PERF_DO_STMT_AND_LOG(unsigned int rand = arc4random(); , "flushUIBlocksOnRootNode(random id:%u", rand); int32_t rootTag = strongRootNode->GetId(); NSArray *previousPendingUIBlocks = _pendingUIBlocks; @@ -689,7 +690,7 @@ - (void)flushUIBlocksOnRootNode:(std::weak_ptr)rootNode { dispatch_async(dispatch_get_main_queue(), ^{ __strong __typeof(weakSelf)strongSelf = weakSelf; if (strongSelf) { - HP_PERF_LOG("flushUIBlocksOnRootNode on main thread(random id:%u)",rand); + TDF_PERF_LOG("flushUIBlocksOnRootNode on main thread(random id:%u)",rand); NSDictionary *viewReg = [strongSelf.viewRegistry componentsForRootTag:@(rootTag)]; @try { for (HippyViewManagerUIBlock block in previousPendingUIBlocks) { @@ -698,11 +699,11 @@ - (void)flushUIBlocksOnRootNode:(std::weak_ptr)rootNode { } @catch (NSException *exception) { HippyLogError(@"Exception thrown while executing UI block: %@", exception); } - HP_PERF_LOG("flushUIBlocksOnRootNode on main thread done, block count:%d(random id:%u)", previousPendingUIBlocks.count, rand); + TDF_PERF_LOG("flushUIBlocksOnRootNode on main thread done, block count:%d(random id:%u)", previousPendingUIBlocks.count, rand); } }); } - HP_PERF_LOG("flushUIBlocksOnRootNode End(random id:%u)",rand); + TDF_PERF_LOG("flushUIBlocksOnRootNode End(random id:%u)",rand); } #pragma mark - @@ -1068,8 +1069,8 @@ - (void)registerExtraComponent:(NSArray *)extraComponents { _extraComponents = extraComponents; } -#pragma mark - -#pragma mark Event Handler + +#pragma mark - Event Handler - (void)addEventName:(const std::string &)name forDomNodeId:(int32_t)node_id onRootNode:(std::weak_ptr)rootNode { @@ -1078,7 +1079,7 @@ - (void)addEventName:(const std::string &)name forDomNodeId:(int32_t)node_id return; } int32_t root_id = strongRootNode->GetId(); - HippyShadowView *renderObject = [self renderObjectForcomponentTag:@(node_id) onRootTag:@(root_id)]; + HippyShadowView *renderObject = [self shadowViewForHippyTag:@(node_id) onRootTag:@(root_id)]; [renderObject addEventName:name]; if (name == hippy::kClickEvent) { [self addUIBlock:^(HippyUIManager *uiManager, NSDictionary *viewRegistry) { @@ -1136,6 +1137,7 @@ - (void)addEventName:(const std::string &)name forDomNodeId:(int32_t)node_id } } +/// Called when creating view from shadowView - (void)addEventNameInMainThread:(const std::string &)name forDomNodeId:(int32_t)node_id onRootNode:(std::weak_ptr)rootNode { @@ -1163,14 +1165,14 @@ - (void)addClickEventListenerForView:(int32_t)componentTag onRootNode:(std::weak return; } int32_t root_id = strongRootNode->GetId(); - UIView *view = [self viewForComponentTag:@(componentTag) onRootTag:@(root_id)]; + UIView *view = [self viewForHippyTag:@(componentTag) onRootTag:@(root_id)]; if (view) { __weak id weakSelf = self; - [view addViewEvent:NativeRenderViewEventTypeClick eventListener:^(CGPoint point, - BOOL canCapture, - BOOL canBubble, - BOOL canBePreventedInCapture, - BOOL canBePreventedInBubbling) { + OnTouchEventHandler eventListener = ^(CGPoint point, + BOOL canCapture, + BOOL canBubble, + BOOL canBePreventedInCapture, + BOOL canBePreventedInBubbling) { id strongSelf = weakSelf; if (strongSelf) { [strongSelf domNodeForComponentTag:componentTag onRootNode:rootNode resultNode:^(std::shared_ptr node) { @@ -1183,7 +1185,9 @@ - (void)addClickEventListenerForView:(int32_t)componentTag onRootNode:(std::weak } }]; } - }]; + }; +// [view addViewEvent:NativeRenderViewEventTypeClick eventListener:eventListener]; + [view setOnClick:eventListener]; } else { } @@ -1196,14 +1200,14 @@ - (void)addLongClickEventListenerForView:(int32_t)componentTag onRootNode:(std:: return; } int32_t root_id = strongRootNode->GetId(); - UIView *view = [self viewForComponentTag:@(componentTag) onRootTag:@(root_id)]; + UIView *view = [self viewForHippyTag:@(componentTag) onRootTag:@(root_id)]; if (view) { __weak id weakSelf = self; - [view addViewEvent:NativeRenderViewEventTypeLongClick eventListener:^(CGPoint point, - BOOL canCapture, - BOOL canBubble, - BOOL canBePreventedInCapture, - BOOL canBePreventedInBubbling) { + OnTouchEventHandler eventListener = ^(CGPoint point, + BOOL canCapture, + BOOL canBubble, + BOOL canBePreventedInCapture, + BOOL canBePreventedInBubbling) { id strongSelf = weakSelf; if (strongSelf) { [strongSelf domNodeForComponentTag:componentTag onRootNode:rootNode resultNode:^(std::shared_ptr node) { @@ -1216,7 +1220,10 @@ - (void)addLongClickEventListenerForView:(int32_t)componentTag onRootNode:(std:: } }]; } - }]; + }; + +// [view addViewEvent:NativeRenderViewEventTypeLongClick eventListener:]; + [view setOnLongClick:eventListener]; } else { } @@ -1231,16 +1238,15 @@ - (void)addPressEventListenerForType:(const std::string &)type } int32_t root_id = strongRootNode->GetId(); AssertMainQueue(); - UIView *view = [self viewForComponentTag:@(componentTag) onRootTag:@(root_id)]; - NativeRenderViewEventType eventType = hippy::kPressIn == type ? NativeRenderViewEventType::NativeRenderViewEventTypePressIn : NativeRenderViewEventType::NativeRenderViewEventTypePressOut; + UIView *view = [self viewForHippyTag:@(componentTag) onRootTag:@(root_id)]; if (view) { std::string block_type = type; __weak id weakSelf = self; - [view addViewEvent:eventType eventListener:^(CGPoint point, - BOOL canCapture, - BOOL canBubble, - BOOL canBePreventedInCapture, - BOOL canBePreventedInBubbling) { + OnTouchEventHandler eventListener = ^(CGPoint point, + BOOL canCapture, + BOOL canBubble, + BOOL canBePreventedInCapture, + BOOL canBePreventedInBubbling) { id strongSelf = weakSelf; if (strongSelf) { [strongSelf domNodeForComponentTag:componentTag onRootNode:rootNode resultNode:^(std::shared_ptr node) { @@ -1253,9 +1259,12 @@ - (void)addPressEventListenerForType:(const std::string &)type } }]; } - }]; - } - else { + }; + if (hippy::kPressIn == type) { + [view setOnPressIn:eventListener]; + } else if (hippy::kPressOut == type) { + [view setOnPressOut:eventListener]; + } } } @@ -1268,26 +1277,15 @@ - (void)addTouchEventListenerForType:(const std::string &)type return; } int32_t root_id = strongRootNode->GetId(); - UIView *view = [self viewForComponentTag:@(componentTag) onRootTag:@(root_id)]; + UIView *view = [self viewForHippyTag:@(componentTag) onRootTag:@(root_id)]; if (view) { - // todo 默认值应该有个值代表未知 - NativeRenderViewEventType event_type = NativeRenderViewEventType::NativeRenderViewEventTypeTouchStart; - if (type == hippy::kTouchStartEvent) { - event_type = NativeRenderViewEventType::NativeRenderViewEventTypeTouchStart; - } else if (type == hippy::kTouchMoveEvent) { - event_type = NativeRenderViewEventType::NativeRenderViewEventTypeTouchMove; - } else if (type == hippy::kTouchEndEvent) { - event_type = NativeRenderViewEventType::NativeRenderViewEventTypeTouchEnd; - } else if (type == hippy::kTouchCancelEvent) { - event_type = NativeRenderViewEventType::NativeRenderViewEventTypeTouchCancel; - } const std::string type_ = type; __weak id weakSelf = self; - [view addViewEvent:event_type eventListener:^(CGPoint point, - BOOL canCapture, - BOOL canBubble, - BOOL canBePreventedInCapture, - BOOL canBePreventedInBubbling) { + OnTouchEventHandler eventListener = ^(CGPoint point, + BOOL canCapture, + BOOL canBubble, + BOOL canBePreventedInCapture, + BOOL canBePreventedInBubbling) { id strongSelf = weakSelf; if (strongSelf) { [strongSelf domNodeForComponentTag:componentTag onRootNode:rootNode resultNode:^(std::shared_ptr node) { @@ -1303,46 +1301,24 @@ - (void)addTouchEventListenerForType:(const std::string &)type } }]; } - }]; - } - else { + }; + if (type == hippy::kTouchStartEvent) { + [view setOnTouchDown:eventListener]; + } else if (type == hippy::kTouchMoveEvent) { + [view setOnTouchMove:eventListener]; + } else if (type == hippy::kTouchEndEvent) { + [view setOnTouchEnd:eventListener]; + } else if (type == hippy::kTouchCancelEvent) { + [view setOnTouchCancel:eventListener]; + } } } - (void)addShowEventListenerForType:(const std::string &)type forView:(int32_t)componentTag onRootNode:(std::weak_ptr)rootNode { - AssertMainQueue(); - auto strongRootNode = rootNode.lock(); - if (!strongRootNode) { - return; - } - int32_t root_id = strongRootNode->GetId(); - UIView *view = [self viewForComponentTag:@(componentTag) onRootTag:@(root_id)]; - if (view) { - NativeRenderViewEventType event_type = hippy::kShowEvent == type ? NativeRenderViewEventTypeShow : NativeRenderViewEventTypeDismiss; - __weak id weakSelf = self; - std::string type_ = type; - [view addViewEvent:event_type eventListener:^(CGPoint point, - BOOL canCapture, - BOOL canBubble, - BOOL canBePreventedInCapture, - BOOL canBePreventedInBubbling) { - id strongSelf = weakSelf; - if (strongSelf) { - [strongSelf domNodeForComponentTag:componentTag onRootNode:rootNode resultNode:^(std::shared_ptr node) { - if (node) { - std::shared_ptr domValue = std::make_shared(true); - auto event = std::make_shared(type_, node, canCapture, canBubble, domValue); - node->HandleEvent(event); - [strongSelf domEventDidHandle:type_ forNode:componentTag onRoot:root_id]; - } - }]; - } - }]; - } - else { - } + // Note: not implemented + // iOS do not have these event. } - (void)removeEventName:(const std::string &)eventName @@ -1352,20 +1328,9 @@ - (void)removeEventName:(const std::string &)eventName if (!strongRootNode) { return; } - int32_t root_id = strongRootNode->GetId(); + int32_t componentTag = node_id; - if (eventName == hippy::kClickEvent || - eventName ==hippy::kLongClickEvent || - eventName == hippy::kTouchStartEvent || eventName == hippy::kTouchMoveEvent || - eventName == hippy::kTouchEndEvent || eventName == hippy::kTouchCancelEvent || - eventName == hippy::kShowEvent || eventName == hippy::kDismissEvent || - eventName == hippy::kPressIn || eventName == hippy::kPressOut) { - std::string name_ = eventName; - [self addUIBlock:^(HippyUIManager *uiManager, NSDictionary *viewRegistry) { - UIView *view = [uiManager viewForComponentTag:@(componentTag) onRootTag:@(root_id)]; - [view removeViewEvent:viewEventTypeFromName(name_.c_str())]; - }]; - } else if (eventName == kVSyncKey) { + if (eventName == kVSyncKey) { std::string name_ = eventName; [self domNodeForComponentTag:node_id onRootNode:rootNode resultNode:^(std::shared_ptr node) { if (node) { @@ -1396,7 +1361,7 @@ - (void)addPropertyEvent:(const std::string &)name forDomNode:(int32_t)node_id return; } int32_t root_id = strongRootNode->GetId(); - UIView *view = [self viewForComponentTag:@(node_id) onRootTag:@(root_id)]; + UIView *view = [self viewForHippyTag:@(node_id) onRootTag:@(root_id)]; if (view) { std::string name_ = name; NSDictionary *componentDataByName = [_componentDataByName copy]; @@ -1521,6 +1486,15 @@ - (HippyUIManager *)uiManager { return nil; } +- (id)customTouchHandler { + return objc_getAssociatedObject(self, @selector(customTouchHandler)); +} + +- (void)setCustomTouchHandler:(id)customTouchHandler { + objc_setAssociatedObject(self, @selector(customTouchHandler), customTouchHandler, OBJC_ASSOCIATION_RETAIN); +} + + @end diff --git a/renderer/native/ios/renderer/NativeRenderManager.mm b/renderer/native/ios/renderer/NativeRenderManager.mm index 914a2d20125..9e34a009df5 100644 --- a/renderer/native/ios/renderer/NativeRenderManager.mm +++ b/renderer/native/ios/renderer/NativeRenderManager.mm @@ -21,10 +21,11 @@ */ #import "HippyUIManager.h" +#import "HippyUIManager+Private.h" #import "NativeRenderManager.h" #import "NativeRenderObjectText.h" #import "RenderVsyncManager.h" -#import "HippyAsserts.h" +#import "HippyAssert.h" #include "dom/dom_manager.h" #include "dom/layout_node.h" @@ -111,10 +112,10 @@ void NativeRenderManager::EndBatch(std::weak_ptr root_node) { @autoreleasepool { - HP_PERF_LOG("NativeRenderManager::EndBatch Begin"); + TDF_PERF_LOG("NativeRenderManager::EndBatch Begin"); HippyAssert(renderImpl_, @"renderImpl_ is null, did you forget to call Initialize()?"); [renderImpl_ batchOnRootNode:root_node]; - HP_PERF_LOG("NativeRenderManager::EndBatch End"); + TDF_PERF_LOG("NativeRenderManager::EndBatch End"); } } diff --git a/renderer/native/ios/renderer/component/image/NativeRenderAnimatedImage.h b/renderer/native/ios/renderer/component/image/HippyAnimatedImage.h similarity index 84% rename from renderer/native/ios/renderer/component/image/NativeRenderAnimatedImage.h rename to renderer/native/ios/renderer/component/image/HippyAnimatedImage.h index b91625b0eae..cfa3a7c8caa 100644 --- a/renderer/native/ios/renderer/component/image/NativeRenderAnimatedImage.h +++ b/renderer/native/ios/renderer/component/image/HippyAnimatedImage.h @@ -22,18 +22,18 @@ #import #import "HippyImageProviderProtocol.h" -#import "NativeRenderAnimatedImageView.h" +#import "HippyAnimatedImageView.h" extern const NSTimeInterval kAnimatedImageDelayTimeIntervalMinimum; // -// An `NativeRenderAnimatedImage`'s job is to deliver frames in a highly performant way and works in conjunction with `NativeRenderAnimatedImageView`. +// An `HippyAnimatedImage`'s job is to deliver frames in a highly performant way and works in conjunction with `HippyAnimatedImageView`. // It subclasses `NSObject` and not `UIImage` because it's only an "image" in the sense that a sea lion is a lion. // It tries to intelligently choose the frame cache size depending on the image and memory situation with the goal to lower CPU usage for smaller // ones, lower memory usage for larger ones and always deliver frames for high performant play-back. Note: `posterImage`, `size`, `loopCount`, // `delayTimes` and `frameCount` don't change after successful initialization. // -@interface NativeRenderAnimatedImage : NSObject +@interface HippyAnimatedImage : NSObject @property (nonatomic, strong, readonly) UIImage *posterImage; // Guaranteed to be loaded; usually equivalent to `-imageLazilyCachedAtIndex:0` @property (nonatomic, assign, readonly) CGSize size; // The `.posterImage`'s `.size` @@ -51,7 +51,7 @@ extern const NSTimeInterval kAnimatedImageDelayTimeIntervalMinimum; // After an initial loading time, depending on `frameCacheSize`, frames should be available immediately from the cache. - (UIImage *)imageLazilyCachedAtIndex:(NSUInteger)index; -// Pass either a `UIImage` or an `NativeRenderAnimatedImage` and get back its size +// Pass either a `UIImage` or an `HippyAnimatedImage` and get back its size + (CGSize)sizeForImage:(id)image; - (UIImage *)imageAtIndex:(NSUInteger)index; @@ -62,7 +62,7 @@ extern const NSTimeInterval kAnimatedImageDelayTimeIntervalMinimum; predrawingEnabled:(BOOL)isPredrawingEnabled; + (instancetype)animatedImageWithAnimatedImageProvider:(id)imageProvider; -// On success, the initializers return an `NativeRenderAnimatedImage` with all fields initialized, on failure they return `nil` and an error will be logged. +// On success, the initializers return an `HippyAnimatedImage` with all fields initialized, on failure they return `nil` and an error will be logged. - (instancetype)initWithAnimatedGIFData:(NSData *)data; // Pass 0 for optimalFrameCacheSize to get the default, predrawing is enabled by default. - (instancetype)initWithAnimatedGIFData:(NSData *)data @@ -84,7 +84,7 @@ typedef NS_ENUM(NSUInteger, RAILogLevel) { RAILogLevelVerbose }; -@interface NativeRenderAnimatedImage (Logging) +@interface HippyAnimatedImage (Logging) + (void)setLogBlock:(void (^)(NSString *logString, RAILogLevel logLevel))logBlock logLevel:(RAILogLevel)logLevel; + (void)logStringFromBlock:(NSString * (^)(void))stringBlock withLevel:(RAILogLevel)level; @@ -92,10 +92,5 @@ typedef NS_ENUM(NSUInteger, RAILogLevel) { @end #define RAILog(logLevel, format, ...) -//[NativeRenderAnimatedImage logStringFromBlock:^NSString *{ return [NSString stringWithFormat:(format), ## __VA_ARGS__]; } withLevel:(logLevel)] +//[HippyAnimatedImage logStringFromBlock:^NSString *{ return [NSString stringWithFormat:(format), ## __VA_ARGS__]; } withLevel:(logLevel)] -@interface NativeRenderWeakProxy : NSProxy - -+ (instancetype)weakProxyForObject:(id)targetObject; - -@end diff --git a/renderer/native/ios/renderer/component/image/NativeRenderAnimatedImage.m b/renderer/native/ios/renderer/component/image/HippyAnimatedImage.m similarity index 88% rename from renderer/native/ios/renderer/component/image/NativeRenderAnimatedImage.m rename to renderer/native/ios/renderer/component/image/HippyAnimatedImage.m index 5eca5aa9c31..39ade947af1 100644 --- a/renderer/native/ios/renderer/component/image/NativeRenderAnimatedImage.m +++ b/renderer/native/ios/renderer/component/image/HippyAnimatedImage.m @@ -20,9 +20,10 @@ * limitations under the License. */ -#import "NativeRenderAnimatedImage.h" +#import "HippyAnimatedImage.h" #import #import +#import "HippyWeakProxy.h" // From vm_param.h, define for iOS 8.0 or higher to build on device. #ifndef BYTE_SIZE @@ -36,24 +37,24 @@ const NSTimeInterval kAnimatedImageDelayTimeIntervalMinimum = 0.02; // An animated image's data size (dimensions * frameCount) category; its value is the max allowed memory (in MB). -// E.g.: A 100x200px GIF with 30 frames is ~2.3MB in our pixel format and would fall into the `NativeRenderAnimatedImageDataSizeCategoryAll` category. -typedef NS_ENUM(NSUInteger, NativeRenderAnimatedImageDataSizeCategory) { - NativeRenderAnimatedImageDataSizeCategoryAll = 10, // All frames permanently in memory (be nice to the CPU) - NativeRenderAnimatedImageDataSizeCategoryDefault +// E.g.: A 100x200px GIF with 30 frames is ~2.3MB in our pixel format and would fall into the `HippyAnimatedImageDataSizeCategoryAll` category. +typedef NS_ENUM(NSUInteger, HippyAnimatedImageDataSizeCategory) { + HippyAnimatedImageDataSizeCategoryAll = 10, // All frames permanently in memory (be nice to the CPU) + HippyAnimatedImageDataSizeCategoryDefault = 75, // A frame cache of default size in memory (usually real-time performance and keeping low memory profile) - NativeRenderAnimatedImageDataSizeCategoryOnDemand = 250, // Only keep one frame at the time in memory (easier on memory, slowest performance) - NativeRenderAnimatedImageDataSizeCategoryUnsupported // Even for one frame too large, computer says no. + HippyAnimatedImageDataSizeCategoryOnDemand = 250, // Only keep one frame at the time in memory (easier on memory, slowest performance) + HippyAnimatedImageDataSizeCategoryUnsupported // Even for one frame too large, computer says no. }; -typedef NS_ENUM(NSUInteger, NativeRenderAnimatedImageFrameCacheSize) { - NativeRenderAnimatedImageFrameCacheSizeNoLimit = 0, // 0 means no specific limit - NativeRenderAnimatedImageFrameCacheSizeLowMemory = 1, // The minimum frame cache size; this will produce frames on-demand. - NativeRenderAnimatedImageFrameCacheSizeGrowAfterMemoryWarning +typedef NS_ENUM(NSUInteger, HippyAnimatedImageFrameCacheSize) { + HippyAnimatedImageFrameCacheSizeNoLimit = 0, // 0 means no specific limit + HippyAnimatedImageFrameCacheSizeLowMemory = 1, // The minimum frame cache size; this will produce frames on-demand. + HippyAnimatedImageFrameCacheSizeGrowAfterMemoryWarning = 2, // If we can produce the frames faster than we consume, one frame ahead will already result in a stutter-free playback. - NativeRenderAnimatedImageFrameCacheSizeDefault = 5 // Build up a comfy buffer window to cope with CPU hiccups etc. + HippyAnimatedImageFrameCacheSizeDefault = 5 // Build up a comfy buffer window to cope with CPU hiccups etc. }; -@interface NativeRenderAnimatedImage () +@interface HippyAnimatedImage () @property (nonatomic, assign, readonly) NSUInteger frameCacheSizeOptimal; // The optimal number of frames to cache based on image size & number of frames; never changes @property (nonatomic, assign, readonly, getter=isPredrawingEnabled) BOOL predrawingEnabled; // Enables predrawing of images to improve performance. @@ -71,8 +72,8 @@ @interface NativeRenderAnimatedImage () // The weak proxy is used to break retain cycles with delayed actions from memory warnings. // We are lying about the actual type here to gain static type checking and eliminate casts. -// The actual type of the object is `NativeRenderWeakProxy`. -@property (nonatomic, strong, readonly) NativeRenderAnimatedImage *weakProxy; +// The actual type of the object is `HippyWeakProxy`. +@property (nonatomic, strong, readonly) HippyAnimatedImage *weakProxy; @property (nonatomic, strong) id imageProvider; @end @@ -80,7 +81,7 @@ @interface NativeRenderAnimatedImage () // For custom dispatching of memory warnings to avoid deallocation races since NSNotificationCenter doesn't retain objects it is notifying. static NSHashTable *allAnimatedImagesWeak; -@implementation NativeRenderAnimatedImage +@implementation HippyAnimatedImage #pragma mark - Accessors #pragma mark Public @@ -90,11 +91,11 @@ - (NSUInteger)frameCacheSizeCurrent { NSUInteger frameCacheSizeCurrent = self.frameCacheSizeOptimal; // If set, respect the caps. - if (self.frameCacheSizeMax > NativeRenderAnimatedImageFrameCacheSizeNoLimit) { + if (self.frameCacheSizeMax > HippyAnimatedImageFrameCacheSizeNoLimit) { frameCacheSizeCurrent = MIN(frameCacheSizeCurrent, self.frameCacheSizeMax); } - if (self.frameCacheSizeMaxInternal > NativeRenderAnimatedImageFrameCacheSizeNoLimit) { + if (self.frameCacheSizeMaxInternal > HippyAnimatedImageFrameCacheSizeNoLimit) { frameCacheSizeCurrent = MIN(frameCacheSizeCurrent, self.frameCacheSizeMaxInternal); } @@ -134,7 +135,7 @@ - (void)setFrameCacheSizeMaxInternal:(NSUInteger)frameCacheSizeMaxInternal { #pragma mark - Life Cycle + (void)initialize { - if (self == [NativeRenderAnimatedImage class]) { + if (self == [HippyAnimatedImage class]) { // UIKit memory warning notification handler shared by all of the instances allAnimatedImagesWeak = [NSHashTable weakObjectsHashTable]; @@ -144,7 +145,7 @@ + (void)initialize { // the main run loop, and we don't lock on allAnimatedImagesWeak NSAssert([NSThread isMainThread], @"Received memory warning on non-main thread"); // Get a strong reference to all of the images. If an instance is returned in this array, it - // is still live and has not entered dealloc. Note that NativeRenderAnimatedImages can be created + // is still live and has not entered dealloc. Note that HippyAnimatedImages can be created // on any thread, so the hash table must be locked. NSArray *images = nil; @synchronized(allAnimatedImagesWeak) { @@ -157,10 +158,10 @@ + (void)initialize { } - (instancetype)init { - NativeRenderAnimatedImage *animatedImage = [self initWithAnimatedGIFData:nil]; + HippyAnimatedImage *animatedImage = [self initWithAnimatedGIFData:nil]; if (!animatedImage) { RAILog(RAILogLevelError, - @"Use `-initWithAnimatedGIFData:` and supply the animated GIF data as an argument to initialize an object of type `NativeRenderAnimatedImage`."); + @"Use `-initWithAnimatedGIFData:` and supply the animated GIF data as an argument to initialize an object of type `HippyAnimatedImage`."); } return animatedImage; } @@ -269,16 +270,16 @@ - (instancetype)initWithAnimatedImageProvider:(id)im // It's only dependent on the image size & number of frames and never changes. CGFloat animatedImageDataSize = CGImageGetBytesPerRow(self.posterImage.CGImage) * self.size.height * (self.frameCount - skippedFrameCount) / MEGABYTE; - if (animatedImageDataSize <= NativeRenderAnimatedImageDataSizeCategoryAll) { + if (animatedImageDataSize <= HippyAnimatedImageDataSizeCategoryAll) { _frameCacheSizeOptimal = self.frameCount; - } else if (animatedImageDataSize <= NativeRenderAnimatedImageDataSizeCategoryDefault) { + } else if (animatedImageDataSize <= HippyAnimatedImageDataSizeCategoryDefault) { // This value doesn't depend on device memory much because if we're not keeping all frames in memory we will always be decoding 1 // frame up ahead per 1 frame that gets played and at this point we might as well just keep a small buffer just large enough to keep // from running out of frames. - _frameCacheSizeOptimal = NativeRenderAnimatedImageFrameCacheSizeDefault; + _frameCacheSizeOptimal = HippyAnimatedImageFrameCacheSizeDefault; } else { // The predicted size exceeds the limits to build up a cache and we go into low memory mode from the beginning. - _frameCacheSizeOptimal = NativeRenderAnimatedImageFrameCacheSizeLowMemory; + _frameCacheSizeOptimal = HippyAnimatedImageFrameCacheSizeLowMemory; } } else { // Use the provided value. @@ -291,10 +292,10 @@ - (instancetype)initWithAnimatedImageProvider:(id)im _allFramesIndexSet = [[NSIndexSet alloc] initWithIndexesInRange:NSMakeRange(0, self.frameCount)]; // See the property declarations for descriptions. - _weakProxy = (id)[NativeRenderWeakProxy weakProxyForObject:self]; + _weakProxy = (id)[HippyWeakProxy weakProxyForObject:self]; // Register this instance in the weak table for memory notifications. The NSHashTable will clean up after itself when we're gone. - // Note that NativeRenderAnimatedImages can be created on any thread, so the hash table must be locked. + // Note that HippyAnimatedImages can be created on any thread, so the hash table must be locked. @synchronized(allAnimatedImagesWeak) { [allAnimatedImagesWeak addObject:self]; } @@ -303,7 +304,7 @@ - (instancetype)initWithAnimatedImageProvider:(id)im } + (instancetype)animatedImageWithAnimatedImageProvider:(id)imageProvider { - NativeRenderAnimatedImage *animatedImage = [[NativeRenderAnimatedImage alloc] initWithAnimatedImageProvider:imageProvider]; + HippyAnimatedImage *animatedImage = [[HippyAnimatedImage alloc] initWithAnimatedImageProvider:imageProvider]; return animatedImage; } @@ -452,16 +453,16 @@ - (instancetype)initWithAnimatedGIFData:(NSData *)data // It's only dependent on the image size & number of frames and never changes. CGFloat animatedImageDataSize = CGImageGetBytesPerRow(self.posterImage.CGImage) * self.size.height * (self.frameCount - skippedFrameCount) / MEGABYTE; - if (animatedImageDataSize <= NativeRenderAnimatedImageDataSizeCategoryAll) { + if (animatedImageDataSize <= HippyAnimatedImageDataSizeCategoryAll) { _frameCacheSizeOptimal = self.frameCount; - } else if (animatedImageDataSize <= NativeRenderAnimatedImageDataSizeCategoryDefault) { + } else if (animatedImageDataSize <= HippyAnimatedImageDataSizeCategoryDefault) { // This value doesn't depend on device memory much because if we're not keeping all frames in memory we will always be decoding 1 // frame up ahead per 1 frame that gets played and at this point we might as well just keep a small buffer just large enough to keep // from running out of frames. - _frameCacheSizeOptimal = NativeRenderAnimatedImageFrameCacheSizeDefault; + _frameCacheSizeOptimal = HippyAnimatedImageFrameCacheSizeDefault; } else { // The predicted size exceeds the limits to build up a cache and we go into low memory mode from the beginning. - _frameCacheSizeOptimal = NativeRenderAnimatedImageFrameCacheSizeLowMemory; + _frameCacheSizeOptimal = HippyAnimatedImageFrameCacheSizeLowMemory; } } else { // Use the provided value. @@ -474,10 +475,10 @@ - (instancetype)initWithAnimatedGIFData:(NSData *)data _allFramesIndexSet = [[NSIndexSet alloc] initWithIndexesInRange:NSMakeRange(0, self.frameCount)]; // See the property declarations for descriptions. - _weakProxy = (id)[NativeRenderWeakProxy weakProxyForObject:self]; + _weakProxy = (id)[HippyWeakProxy weakProxyForObject:self]; // Register this instance in the weak table for memory notifications. The NSHashTable will clean up after itself when we're gone. - // Note that NativeRenderAnimatedImages can be created on any thread, so the hash table must be locked. + // Note that HippyAnimatedImages can be created on any thread, so the hash table must be locked. @synchronized(allAnimatedImagesWeak) { [allAnimatedImagesWeak addObject:self]; } @@ -486,7 +487,7 @@ - (instancetype)initWithAnimatedGIFData:(NSData *)data } + (instancetype)animatedImageWithGIFData:(NSData *)data { - NativeRenderAnimatedImage *animatedImage = [[NativeRenderAnimatedImage alloc] initWithAnimatedGIFData:data]; + HippyAnimatedImage *animatedImage = [[HippyAnimatedImage alloc] initWithAnimatedGIFData:data]; return animatedImage; } @@ -560,7 +561,7 @@ - (void)addFrameIndexesToCache:(NSIndexSet *)frameIndexesToAddToCache { // Start streaming requested frames in the background into the cache. // Avoid capturing self in the block as there's no reason to keep doing work if the animated image went away. - NativeRenderAnimatedImage *__weak weakSelf = self; + HippyAnimatedImage *__weak weakSelf = self; dispatch_async(self.serialQueue, ^{ // Produce and cache next needed frame. void (^frameRangeBlock)(NSRange, BOOL *) = ^(NSRange range, BOOL *stop) { @@ -627,12 +628,12 @@ + (CGSize)sizeForImage:(id)image { if ([image isKindOfClass:[UIImage class]]) { UIImage *uiImage = (UIImage *)image; imageSize = uiImage.size; - } else if ([image isKindOfClass:[NativeRenderAnimatedImage class]]) { - NativeRenderAnimatedImage *animatedImage = (NativeRenderAnimatedImage *)image; + } else if ([image isKindOfClass:[HippyAnimatedImage class]]) { + HippyAnimatedImage *animatedImage = (HippyAnimatedImage *)image; imageSize = animatedImage.size; } else { // Bear trap to capture bad images; we have seen crashers cropping up on iOS 7. - RAILog(RAILogLevelError, @"`image` isn't of expected types `UIImage` or `NativeRenderAnimatedImage`: %@", image); + RAILog(RAILogLevelError, @"`image` isn't of expected types `UIImage` or `HippyAnimatedImage`: %@", image); } return imageSize; @@ -728,7 +729,7 @@ - (void)growFrameCacheSizeAfterMemoryWarning:(NSNumber *)frameCacheSize { } - (void)resetFrameCacheSizeMaxInternal { - self.frameCacheSizeMaxInternal = NativeRenderAnimatedImageFrameCacheSizeNoLimit; + self.frameCacheSizeMaxInternal = HippyAnimatedImageFrameCacheSizeNoLimit; RAILog(RAILogLevelDebug, @"Reset frame cache size max (current frame cache size: %lu) for animated image: %@", (unsigned long)self.frameCacheSizeCurrent, self); } @@ -740,15 +741,15 @@ - (void)didReceiveMemoryWarning:(NSNotification *)notification { // If we were about to grow larger, but got rapped on our knuckles by the system again, cancel. [NSObject cancelPreviousPerformRequestsWithTarget:self.weakProxy selector:@selector(growFrameCacheSizeAfterMemoryWarning:) - object:@(NativeRenderAnimatedImageFrameCacheSizeGrowAfterMemoryWarning)]; + object:@(HippyAnimatedImageFrameCacheSizeGrowAfterMemoryWarning)]; [NSObject cancelPreviousPerformRequestsWithTarget:self.weakProxy selector:@selector(resetFrameCacheSizeMaxInternal) object:nil]; // Go down to the minimum and by that implicitly immediately purge from the cache if needed to not get jettisoned by the system and start // producing frames on-demand. RAILog(RAILogLevelDebug, @"Attempt setting frame cache size max to %lu (previous was %lu) after memory warning #%lu for animated image: %@", - (unsigned long)NativeRenderAnimatedImageFrameCacheSizeLowMemory, (unsigned long)self.frameCacheSizeMaxInternal, + (unsigned long)HippyAnimatedImageFrameCacheSizeLowMemory, (unsigned long)self.frameCacheSizeMaxInternal, (unsigned long)self.memoryWarningCount, self); - self.frameCacheSizeMaxInternal = NativeRenderAnimatedImageFrameCacheSizeLowMemory; + self.frameCacheSizeMaxInternal = HippyAnimatedImageFrameCacheSizeLowMemory; // Schedule growing larger again after a while, but cap our attempts to prevent a periodic sawtooth wave (ramps upward and then sharply drops) of // memory usage. @@ -767,7 +768,7 @@ - (void)didReceiveMemoryWarning:(NSNotification *)notification { const NSTimeInterval kGrowDelay = 2.0; if ((self.memoryWarningCount - 1) <= kGrowAttemptsMax) { [self.weakProxy performSelector:@selector(growFrameCacheSizeAfterMemoryWarning:) - withObject:@(NativeRenderAnimatedImageFrameCacheSizeGrowAfterMemoryWarning) + withObject:@(HippyAnimatedImageFrameCacheSizeGrowAfterMemoryWarning) afterDelay:kGrowDelay]; } @@ -870,7 +871,7 @@ - (NSString *)description { #pragma mark - Logging -@implementation NativeRenderAnimatedImage (Logging) +@implementation HippyAnimatedImage (Logging) static void (^_logBlock)(NSString *logString, RAILogLevel logLevel) = nil; static RAILogLevel _logLevel; @@ -888,52 +889,3 @@ + (void)logStringFromBlock:(NSString * (^)(void))stringBlock withLevel:(RAILogLe @end -#pragma mark - NativeRenderWeakProxy - -@interface NativeRenderWeakProxy () - -@property (nonatomic, weak) id target; - -@end - -@implementation NativeRenderWeakProxy - -#pragma mark Life Cycle - -// This is the designated creation method of an `NativeRenderWeakProxy` and -// as a subclass of `NSProxy` it doesn't respond to or need `-init`. -+ (instancetype)weakProxyForObject:(id)targetObject { - NativeRenderWeakProxy *weakProxy = [NativeRenderWeakProxy alloc]; - weakProxy.target = targetObject; - return weakProxy; -} - -#pragma mark Forwarding Messages - -- (id)forwardingTargetForSelector:(SEL)selector { - // Keep it lightweight: access the ivar directly - return _target; -} - -#pragma mark - NSWeakProxy Method Overrides -#pragma mark Handling Unimplemented Methods - -- (void)forwardInvocation:(NSInvocation *)invocation { - // Fallback for when target is nil. Don't do anything, just return 0/NULL/nil. - // The method signature we've received to get here is just a dummy to keep `doesNotRecognizeSelector:` from firing. - // We can't really handle struct return types here because we don't know the length. - void *nullPointer = NULL; - [invocation setReturnValue:&nullPointer]; -} - -- (NSMethodSignature *)methodSignatureForSelector:(SEL)selector { - // We only get here if `forwardingTargetForSelector:` returns nil. - // In that case, our weak target has been reclaimed. Return a dummy method signature to keep `doesNotRecognizeSelector:` from firing. - // We'll emulate the Obj-c messaging nil behavior by setting the return value to nil in `forwardInvocation:`, but we'll assume that the return - // value is `sizeof(void *)`. Other libraries handle this situation by making use of a global method signature cache, but that seems heavier than - // necessary and has issues as well. See https://www.mikeash.com/pyblog/friday-qa-2010-02-26-futures.html and - // https://github.com/steipete/PSTDelegateProxy/issues/1 for examples of using a method signature cache. - return [NSObject instanceMethodSignatureForSelector:@selector(init)]; -} - -@end diff --git a/renderer/native/ios/renderer/component/image/NativeRenderAnimatedImageView.h b/renderer/native/ios/renderer/component/image/HippyAnimatedImageView.h similarity index 86% rename from renderer/native/ios/renderer/component/image/NativeRenderAnimatedImageView.h rename to renderer/native/ios/renderer/component/image/HippyAnimatedImageView.h index ee736cd90fa..69771815e52 100644 --- a/renderer/native/ios/renderer/component/image/NativeRenderAnimatedImageView.h +++ b/renderer/native/ios/renderer/component/image/HippyAnimatedImageView.h @@ -22,19 +22,19 @@ #import -@class NativeRenderAnimatedImage; +@class HippyAnimatedImage; // -// An `NativeRenderAnimatedImageView` can take an `NativeRenderAnimatedImage` and plays it automatically when in view hierarchy and stops when removed. +// An `HippyAnimatedImageView` can take an `HippyAnimatedImage` and plays it automatically when in view hierarchy and stops when removed. // The animation can also be controlled with the `UIImageView` methods `-start/stop/isAnimating`. // It is a fully compatible `UIImageView` subclass and can be used as a drop-in component to work with existing code paths expecting to display a // `UIImage`. Under the hood it uses a `CADisplayLink` for playback, which can be inspected with `currentFrame` & `currentFrameIndex`. // -@interface NativeRenderAnimatedImageView : UIImageView +@interface HippyAnimatedImageView : UIImageView // Setting `[UIImageView.image]` to a non-`nil` value clears out existing `animatedImage`. // And vice versa, setting `animatedImage` will initially populate the `[UIImageView.image]` to its `posterImage` and then start animating and hold // `currentFrame`. -@property (nonatomic, strong) NativeRenderAnimatedImage *animatedImage; +@property (nonatomic, strong) HippyAnimatedImage *animatedImage; @property (nonatomic, copy) void (^loopCompletionBlock)(NSUInteger loopCountRemaining); @property (nonatomic, strong, readonly) UIImage *currentFrame; diff --git a/renderer/native/ios/renderer/component/image/NativeRenderAnimatedImageView.m b/renderer/native/ios/renderer/component/image/HippyAnimatedImageView.m similarity index 97% rename from renderer/native/ios/renderer/component/image/NativeRenderAnimatedImageView.m rename to renderer/native/ios/renderer/component/image/HippyAnimatedImageView.m index 7c860bf3fa2..0427b5401d4 100644 --- a/renderer/native/ios/renderer/component/image/NativeRenderAnimatedImageView.m +++ b/renderer/native/ios/renderer/component/image/HippyAnimatedImageView.m @@ -20,10 +20,11 @@ * limitations under the License. */ -#import "NativeRenderAnimatedImageView.h" -#import "NativeRenderAnimatedImage.h" +#import "HippyAnimatedImageView.h" +#import "HippyAnimatedImage.h" +#import "HippyWeakProxy.h" -@interface NativeRenderAnimatedImageView () +@interface HippyAnimatedImageView () // Override of public `readonly` properties as private `readwrite` @property (nonatomic, strong, readwrite) UIImage *currentFrame; @@ -39,7 +40,7 @@ @interface NativeRenderAnimatedImageView () @end -@implementation NativeRenderAnimatedImageView +@implementation HippyAnimatedImageView @synthesize runLoopMode = _runLoopMode; #pragma mark - Initializers @@ -90,7 +91,7 @@ - (void)commonInit { #pragma mark - Accessors #pragma mark Public -- (void)setAnimatedImage:(NativeRenderAnimatedImage *)animatedImage { +- (void)setAnimatedImage:(HippyAnimatedImage *)animatedImage { if (![_animatedImage isEqual:animatedImage]) { if (animatedImage) { // Clear out the image. @@ -268,7 +269,7 @@ - (void)startAnimating { // will retain its target until it is invalidated. We use a weak proxy so that the image view will get deallocated // independent of the display link's lifetime. Upon image view deallocation, we invalidate the display // link which will lead to the deallocation of both the display link and the weak proxy. - NativeRenderWeakProxy *weakProxy = [NativeRenderWeakProxy weakProxyForObject:self]; + HippyWeakProxy *weakProxy = [HippyWeakProxy weakProxyForObject:self]; self.displayLink = [CADisplayLink displayLinkWithTarget:weakProxy selector:@selector(displayDidRefresh:)]; [self.displayLink addToRunLoop:[NSRunLoop mainRunLoop] forMode:self.runLoopMode]; diff --git a/renderer/native/ios/renderer/component/image/NativeRenderImageView.h b/renderer/native/ios/renderer/component/image/HippyImageView.h similarity index 63% rename from renderer/native/ios/renderer/component/image/NativeRenderImageView.h rename to renderer/native/ios/renderer/component/image/HippyImageView.h index a1be1f2e6ef..ff29704d6f7 100644 --- a/renderer/native/ios/renderer/component/image/NativeRenderImageView.h +++ b/renderer/native/ios/renderer/component/image/HippyImageView.h @@ -23,41 +23,41 @@ #import #import "HippyComponent.h" #import "HippyConvert.h" -#import "NativeRenderAnimatedImageView.h" +#import "HippyAnimatedImageView.h" #import "HippyImageProviderProtocol.h" -@class NativeRenderImageView; +@class HippyImageView; -@interface NativeRenderAnimatedImageOperation : NSOperation { +@interface HippyAnimatedImageOperation : NSOperation { NSData *_animatedImageData; NSString *_url; - __weak NativeRenderImageView *_imageView; + __weak HippyImageView *_imageView; id _imageProvider; } -- (id)initWithAnimatedImageData:(NSData *)data imageView:(NativeRenderImageView *)imageView imageURL:(NSString *)url; -- (id)initWithAnimatedImageProvider:(id)imageProvider imageView:(NativeRenderImageView *)imageView imageURL:(NSString *)url; +- (id)initWithAnimatedImageData:(NSData *)data imageView:(HippyImageView *)imageView imageURL:(NSString *)url; +- (id)initWithAnimatedImageProvider:(id)imageProvider imageView:(HippyImageView *)imageView imageURL:(NSString *)url; @end -typedef NS_ENUM(NSInteger, NativeRenderResizeMode) { - NativeRenderResizeModeCover = UIViewContentModeScaleAspectFill, - NativeRenderResizeModeContain = UIViewContentModeScaleAspectFit, - NativeRenderResizeModeStretch = UIViewContentModeScaleToFill, - NativeRenderResizeModeCenter = UIViewContentModeCenter, - NativeRenderResizeModeRepeat = -1, // Use negative values to avoid conflicts with iOS enum values. +typedef NS_ENUM(NSInteger, HippyResizeMode) { + HippyResizeModeCover = UIViewContentModeScaleAspectFill, + HippyResizeModeContain = UIViewContentModeScaleAspectFit, + HippyResizeModeStretch = UIViewContentModeScaleToFill, + HippyResizeModeCenter = UIViewContentModeCenter, + HippyResizeModeRepeat = -1, // Use negative values to avoid conflicts with iOS enum values. }; -typedef NS_ENUM(NSInteger, NativeRenderShapeMode) { - NativeRenderResizeModeDefalt = 0, - NativeRenderResizeModeCircle, +typedef NS_ENUM(NSInteger, HippyShapeMode) { + HippyResizeModeDefalt = 0, + HippyResizeModeCircle, }; -@interface NativeRenderImageView : NativeRenderAnimatedImageView +@interface HippyImageView : HippyAnimatedImageView @property (nonatomic, assign) CGFloat blurRadius; @property (nonatomic, assign) UIEdgeInsets capInsets; -@property (nonatomic, assign) NativeRenderResizeMode resizeMode; +@property (nonatomic, assign) HippyResizeMode resizeMode; @property (nonatomic, copy) NSArray *source; @property (nonatomic, strong) UIImage *defaultImage; @property (nonatomic, assign) UIImageRenderingMode renderingMode; @@ -67,7 +67,7 @@ typedef NS_ENUM(NSInteger, NativeRenderShapeMode) { @property (nonatomic, assign) CGFloat borderBottomLeftRadius; @property (nonatomic, assign) CGFloat borderBottomRightRadius; @property (nonatomic, assign) CGFloat borderRadius; -@property (nonatomic, assign) NativeRenderShapeMode shape; +@property (nonatomic, assign) HippyShapeMode shape; @property (nonatomic, copy) HippyDirectEventBlock onLoadStart; @property (nonatomic, copy) HippyDirectEventBlock onProgress; @@ -86,9 +86,9 @@ typedef NS_ENUM(NSInteger, NativeRenderShapeMode) { - (BOOL)needsUpdateCornerRadiusManully; @end -@interface HippyConvert (NativeRenderResizeMode) +@interface HippyConvert (HippyResizeMode) -+ (NativeRenderResizeMode)NativeRenderResizeMode:(id)json; -+ (NativeRenderShapeMode)NativeRenderShapeMode:(id)json; ++ (HippyResizeMode)HippyResizeMode:(id)json; ++ (HippyShapeMode)HippyShapeMode:(id)json; @end diff --git a/renderer/native/ios/renderer/component/image/NativeRenderImageView.m b/renderer/native/ios/renderer/component/image/HippyImageView.m similarity index 90% rename from renderer/native/ios/renderer/component/image/NativeRenderImageView.m rename to renderer/native/ios/renderer/component/image/HippyImageView.m index c983bd9d716..8638dfda40d 100644 --- a/renderer/native/ios/renderer/component/image/NativeRenderImageView.m +++ b/renderer/native/ios/renderer/component/image/HippyImageView.m @@ -22,13 +22,13 @@ #import -#import "HippyAsserts.h" +#import "HippyAssert.h" #import "HippyUtils.h" -#import "NativeRenderImageView.h" -#import "NativeRenderAnimatedImage.h" +#import "HippyImageView.h" +#import "HippyAnimatedImage.h" #import "UIView+MountEvent.h" -NSString *const NativeRenderImageErrorDomain = @"NativeRenderImageErrorDomain"; +NSString *const HippyImageErrorDomain = @"HippyImageErrorDomain"; typedef NS_ENUM(NSUInteger, ImageDataError) { ImageDataUnavailable = 10001, @@ -54,7 +54,7 @@ typedef NS_ENUM(NSUInteger, ImageDataError) { return _animatedImageOQ; } -static BOOL NativeRenderImageNeedsShrinkForSize(UIImage *inputImage, CGSize size) { +static BOOL HippyImageNeedsShrinkForSize(UIImage *inputImage, CGSize size) { CGSize inputImageSize = inputImage.size; if (inputImageSize.width > size.width || inputImageSize.height > size.height) { return YES; @@ -62,7 +62,7 @@ static BOOL NativeRenderImageNeedsShrinkForSize(UIImage *inputImage, CGSize size return NO; } -UIImage *NativeRenderBlurredImageWithRadiusv(UIImage *inputImage, CGFloat radius, NSError **error) { +UIImage *HippyBlurredImageWithRadiusv(UIImage *inputImage, CGFloat radius, NSError **error) { CGImageRef imageRef = inputImage.CGImage; CGFloat imageScale = inputImage.scale; UIImageOrientation imageOrientation = inputImage.imageOrientation; @@ -187,17 +187,17 @@ static BOOL NativeRenderImageNeedsShrinkForSize(UIImage *inputImage, CGSize size } if (error) { NSDictionary *useInfo = @{NSLocalizedDescriptionKey: exception.reason ?: @""}; - *error = [NSError errorWithDomain:NativeRenderImageErrorDomain code:ImageDataBlurredError userInfo:useInfo]; + *error = [NSError errorWithDomain:HippyImageErrorDomain code:ImageDataBlurredError userInfo:useInfo]; } return inputImage; } } NSError *imageErrorFromParams(NSInteger errorCode, NSString *errorDescription) { - return [NSError errorWithDomain:NativeRenderImageErrorDomain code:errorCode userInfo:@ { NSLocalizedDescriptionKey: errorDescription ?: @"" }]; + return [NSError errorWithDomain:HippyImageErrorDomain code:errorCode userInfo:@ { NSLocalizedDescriptionKey: errorDescription ?: @"" }]; } -@interface NativeRenderImageView () { +@interface HippyImageView () { __weak CALayer *_borderWidthLayer; BOOL _needsUpdateBorderRadiusManully; BOOL _needsReloadImage; @@ -205,13 +205,13 @@ @interface NativeRenderImageView () { id _imageProvider; } -@property (nonatomic) NativeRenderAnimatedImageOperation *animatedImageOperation; +@property (nonatomic) HippyAnimatedImageOperation *animatedImageOperation; @property (atomic, strong) NSString *pendingImageSourceUri; // The image source that's being loaded from the network @property (atomic, strong) NSString *imageSourceUri; // The image source that's currently displayed @end -@implementation NativeRenderImageView +@implementation HippyImageView - (instancetype)initWithFrame:(CGRect)frame { if (self = [super initWithFrame:frame]) { @@ -230,6 +230,15 @@ - (void)dealloc { [[NSNotificationCenter defaultCenter] removeObserver:self]; } +- (void)setTintColor:(UIColor *)tintColor{ + [super setTintColor:tintColor]; + if(_animatedImageOperation){ + _needsReloadImage = YES; + }else{ + _needsUpdateImage = YES; + } +} + - (void)didMoveToWindow { [super didMoveToWindow]; if (!self.window) { @@ -311,19 +320,19 @@ - (void)setFrame:(CGRect)frame { [self updateCornerRadius]; } -- (void)setShape:(NativeRenderShapeMode)shape { +- (void)setShape:(HippyShapeMode)shape { if (shape != _shape) { _shape = shape; - if (shape == NativeRenderResizeModeCircle) { + if (shape == HippyResizeModeCircle) { self.contentMode = UIViewContentModeScaleAspectFit; } } } -- (void)setResizeMode:(NativeRenderResizeMode)resizeMode { +- (void)setResizeMode:(HippyResizeMode)resizeMode { if (_resizeMode != resizeMode) { _resizeMode = resizeMode; - if (_resizeMode == NativeRenderResizeModeRepeat) { + if (_resizeMode == HippyResizeModeRepeat) { self.contentMode = UIViewContentModeScaleToFill; } else { self.contentMode = (UIViewContentMode)resizeMode; @@ -381,7 +390,7 @@ - (void)reloadImage { [_animatedImageOperation cancel]; } _animatedImageOperation = - [[NativeRenderAnimatedImageOperation alloc] initWithAnimatedImageProvider:_imageProvider + [[HippyAnimatedImageOperation alloc] initWithAnimatedImageProvider:_imageProvider imageView:self imageURL:imagePath]; [animated_image_queue() addOperation:_animatedImageOperation]; @@ -423,7 +432,7 @@ - (void)loadImage:(UIImage *)image url:(NSString *)url error:(NSError *)error ne } __weak __typeof(self) weakSelf = self; void (^setImageBlock)(UIImage *) = ^(UIImage *image) { - NativeRenderImageView *strongSelf = weakSelf; + HippyImageView *strongSelf = weakSelf; if (!strongSelf) { return; } @@ -446,7 +455,7 @@ - (void)loadImage:(UIImage *)image url:(NSString *)url error:(NSError *)error ne // Blur on a background thread to avoid blocking interaction dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{ NSError *error = nil; - UIImage *blurredImage = NativeRenderBlurredImageWithRadiusv(image, br, &error); + UIImage *blurredImage = HippyBlurredImageWithRadiusv(image, br, &error); if (error) { NSError *finalError = HippyErrorFromErrorAndModuleName(error, @"unknown"); HippyFatal(finalError); @@ -474,11 +483,11 @@ - (void)updateImage:(UIImage *)image { image = [image imageWithRenderingMode:_renderingMode]; } - if (NativeRenderResizeModeRepeat == _resizeMode) { + if (HippyResizeModeRepeat == _resizeMode) { image = [image resizableImageWithCapInsets:_capInsets resizingMode:UIImageResizingModeTile]; } - else if (NativeRenderResizeModeCenter == _resizeMode) { - if (NativeRenderImageNeedsShrinkForSize(image, self.bounds.size)) { + else if (HippyResizeModeCenter == _resizeMode) { + if (HippyImageNeedsShrinkForSize(image, self.bounds.size)) { self.contentMode = UIViewContentModeScaleAspectFit; } } @@ -487,7 +496,7 @@ - (void)updateImage:(UIImage *)image { image = [image resizableImageWithCapInsets:_capInsets resizingMode:UIImageResizingModeStretch]; } - if (_shape == NativeRenderResizeModeCircle) { + if (_shape == HippyResizeModeCircle) { image = [self circleImage:image]; } @@ -668,27 +677,27 @@ - (BorderRadiusStruct)properBorderRadius { @end -@implementation HippyConvert (NativeRenderResizeMode) +@implementation HippyConvert (HippyResizeMode) -HP_ENUM_CONVERTER(NativeRenderResizeMode, (@{ - @"cover": @(NativeRenderResizeModeCover), - @"contain": @(NativeRenderResizeModeContain), - @"stretch": @(NativeRenderResizeModeStretch), - @"center": @(NativeRenderResizeModeCenter), - @"repeat": @(NativeRenderResizeModeRepeat), +HP_ENUM_CONVERTER(HippyResizeMode, (@{ + @"cover": @(HippyResizeModeCover), + @"contain": @(HippyResizeModeContain), + @"stretch": @(HippyResizeModeStretch), + @"center": @(HippyResizeModeCenter), + @"repeat": @(HippyResizeModeRepeat), }), - NativeRenderResizeModeStretch, integerValue) + HippyResizeModeStretch, integerValue) -HP_ENUM_CONVERTER(NativeRenderShapeMode, (@{ - @"normal": @(NativeRenderResizeModeDefalt), - @"circle": @(NativeRenderResizeModeCircle) -}), NativeRenderResizeModeDefalt, integerValue) +HP_ENUM_CONVERTER(HippyShapeMode, (@{ + @"normal": @(HippyResizeModeDefalt), + @"circle": @(HippyResizeModeCircle) +}), HippyResizeModeDefalt, integerValue) @end -@implementation NativeRenderAnimatedImageOperation +@implementation HippyAnimatedImageOperation -- (id)initWithAnimatedImageProvider:(id)imageProvider imageView:(NativeRenderImageView *)imageView imageURL:(NSString *)url { +- (id)initWithAnimatedImageProvider:(id)imageProvider imageView:(HippyImageView *)imageView imageURL:(NSString *)url { self = [super init]; if (self) { _imageProvider = imageProvider; @@ -698,7 +707,7 @@ - (id)initWithAnimatedImageProvider:(id)imageProvide return self; } -- (id)initWithAnimatedImageData:(NSData *)data imageView:(NativeRenderImageView *)imageView imageURL:(NSString *)url { +- (id)initWithAnimatedImageData:(NSData *)data imageView:(HippyImageView *)imageView imageURL:(NSString *)url { self = [super init]; if (self) { _animatedImageData = data; @@ -710,17 +719,17 @@ - (id)initWithAnimatedImageData:(NSData *)data imageView:(NativeRenderImageView - (void)main { if (![self isCancelled] && (_animatedImageData || _imageProvider) && _imageView) { - NativeRenderAnimatedImage *animatedImage = nil; + HippyAnimatedImage *animatedImage = nil; if (_imageProvider) { - animatedImage = [NativeRenderAnimatedImage animatedImageWithAnimatedImageProvider:_imageProvider]; + animatedImage = [HippyAnimatedImage animatedImageWithAnimatedImageProvider:_imageProvider]; } else if (_animatedImageData) { - animatedImage = [NativeRenderAnimatedImage animatedImageWithGIFData:_animatedImageData]; + animatedImage = [HippyAnimatedImage animatedImageWithGIFData:_animatedImageData]; } if (![self isCancelled] && _imageView) { - __weak NativeRenderImageView *wIV = _imageView; + __weak HippyImageView *wIV = _imageView; __weak NSString *wURL = _url; dispatch_async(dispatch_get_main_queue(), ^{ - NativeRenderImageView *sIV = wIV; + HippyImageView *sIV = wIV; NSString *sURL = wURL; if (sIV && sURL) { [sIV loadImage:animatedImage.posterImage url:sURL error:nil needBlur:YES]; diff --git a/renderer/native/ios/renderer/component/image/NativeRenderImageViewManager.h b/renderer/native/ios/renderer/component/image/HippyImageViewManager.h similarity index 93% rename from renderer/native/ios/renderer/component/image/NativeRenderImageViewManager.h rename to renderer/native/ios/renderer/component/image/HippyImageViewManager.h index 417a8a65226..48806f0c6c7 100644 --- a/renderer/native/ios/renderer/component/image/NativeRenderImageViewManager.h +++ b/renderer/native/ios/renderer/component/image/HippyImageViewManager.h @@ -22,6 +22,6 @@ #import "HippyViewManager.h" -@interface NativeRenderImageViewManager : HippyViewManager +@interface HippyImageViewManager : HippyViewManager @end diff --git a/renderer/native/ios/renderer/component/image/NativeRenderImageViewManager.mm b/renderer/native/ios/renderer/component/image/HippyImageViewManager.mm similarity index 70% rename from renderer/native/ios/renderer/component/image/NativeRenderImageViewManager.mm rename to renderer/native/ios/renderer/component/image/HippyImageViewManager.mm index 603048f7b2f..69dc1227d1a 100644 --- a/renderer/native/ios/renderer/component/image/NativeRenderImageViewManager.mm +++ b/renderer/native/ios/renderer/component/image/HippyImageViewManager.mm @@ -20,27 +20,22 @@ * limitations under the License. */ -#import "HippyAsserts.h" +#import "HippyAssert.h" #import "HippyUtils.h" -#import "NativeRenderImageViewManager.h" -#import "NativeRenderImageView.h" +#import "HippyImageViewManager.h" +#import "HippyImageView.h" #import "HippyUIManager.h" #import "TypeConverter.h" - #include "VFSUriLoader.h" -@interface NativeRenderImageViewManager () { -} - -@end -@implementation NativeRenderImageViewManager +@implementation HippyImageViewManager HIPPY_EXPORT_MODULE(Image); HIPPY_EXPORT_VIEW_PROPERTY(blurRadius, CGFloat) HIPPY_EXPORT_VIEW_PROPERTY(capInsets, UIEdgeInsets) -HIPPY_EXPORT_VIEW_PROPERTY(resizeMode, NativeRenderResizeMode) +HIPPY_EXPORT_VIEW_PROPERTY(resizeMode, HippyResizeMode) HIPPY_EXPORT_VIEW_PROPERTY(onLoadStart, HippyDirectEventBlock) HIPPY_EXPORT_VIEW_PROPERTY(onProgress, HippyDirectEventBlock) HIPPY_EXPORT_VIEW_PROPERTY(onError, HippyDirectEventBlock) @@ -48,13 +43,13 @@ @implementation NativeRenderImageViewManager HIPPY_EXPORT_VIEW_PROPERTY(onLoad, HippyDirectEventBlock) HIPPY_EXPORT_VIEW_PROPERTY(onLoadEnd, HippyDirectEventBlock) HIPPY_EXPORT_VIEW_PROPERTY(downSample, BOOL) -HIPPY_EXPORT_VIEW_PROPERTY(shape, NativeRenderShapeMode) -HIPPY_CUSTOM_VIEW_PROPERTY(src, NSString, NativeRenderImageView) { +HIPPY_EXPORT_VIEW_PROPERTY(shape, HippyShapeMode) +HIPPY_CUSTOM_VIEW_PROPERTY(src, NSString, HippyImageView) { NSString *path = [HippyConvert NSString:json]; [self loadImageSource:path forView:view]; } -HIPPY_CUSTOM_VIEW_PROPERTY(source, NSArray, NativeRenderImageView) { +HIPPY_CUSTOM_VIEW_PROPERTY(source, NSArray, HippyImageView) { NSArray *pathSources = [HippyConvert NSArray:json]; if ([pathSources isKindOfClass:[NSArray class]]) { NSDictionary *dicSource = [pathSources firstObject]; @@ -63,12 +58,12 @@ @implementation NativeRenderImageViewManager } } -- (void)loadImageSource:(NSString *)path forView:(NativeRenderImageView *)view { +- (void)loadImageSource:(NSString *)path forView:(HippyImageView *)view { if (!path || !view) { return; } NSString *standardizeAssetUrlString = path; - __weak NativeRenderImageView *weakView = view; + __weak HippyImageView *weakView = view; auto loader = [self.bridge.uiManager VFSUriLoader].lock(); if (!loader) { return; @@ -89,7 +84,7 @@ - (void)loadImageSource:(NSString *)path forView:(NativeRenderImageView *)view { imageProvider.imageDataPath = standardizeAssetUrlString; [imageProvider setImageData:data]; dispatch_async(dispatch_get_main_queue(), ^{ - NativeRenderImageView *strongView = weakView; + HippyImageView *strongView = weakView; if (strongView) { [strongView setImageProvider:imageProvider]; [strongView reloadImage]; @@ -99,30 +94,30 @@ - (void)loadImageSource:(NSString *)path forView:(NativeRenderImageView *)view { }); } -HIPPY_CUSTOM_VIEW_PROPERTY(tintColor, UIColor, NativeRenderImageView) { +HIPPY_CUSTOM_VIEW_PROPERTY(tintColor, UIColor, HippyImageView) { view.tintColor = [HippyConvert UIColor:json] ?: defaultView.tintColor; view.renderingMode = json ? UIImageRenderingModeAlwaysTemplate : defaultView.renderingMode; } -HIPPY_CUSTOM_VIEW_PROPERTY(defaultSource, NSString, NativeRenderImageView) { +HIPPY_CUSTOM_VIEW_PROPERTY(defaultSource, NSString, HippyImageView) { NSString *source = [HippyConvert NSString:json]; [self loadImageSource:source forView:view]; } -#define NATIVE_RENDER_VIEW_BORDER_RADIUS_PROPERTY(SIDE) \ - HIPPY_CUSTOM_VIEW_PROPERTY(border##SIDE##Radius, CGFloat, NativeRenderImageView) { \ - if ([view respondsToSelector:@selector(setBorder##SIDE##Radius:)]) { \ - view.border##SIDE##Radius = json ? [HippyConvert CGFloat:json] : defaultView.border##SIDE##Radius; \ - } \ +#define HIPPY_VIEW_BORDER_RADIUS_PROPERTY(SIDE) \ + HIPPY_CUSTOM_VIEW_PROPERTY(border##SIDE##Radius, CGFloat, HippyImageView) { \ + if ([view respondsToSelector:@selector(setBorder##SIDE##Radius:)]) { \ + view.border##SIDE##Radius = json ? [HippyConvert CGFloat:json] : defaultView.border##SIDE##Radius; \ + } \ } -NATIVE_RENDER_VIEW_BORDER_RADIUS_PROPERTY(TopLeft) -NATIVE_RENDER_VIEW_BORDER_RADIUS_PROPERTY(TopRight) -NATIVE_RENDER_VIEW_BORDER_RADIUS_PROPERTY(BottomLeft) -NATIVE_RENDER_VIEW_BORDER_RADIUS_PROPERTY(BottomRight) +HIPPY_VIEW_BORDER_RADIUS_PROPERTY(TopLeft) +HIPPY_VIEW_BORDER_RADIUS_PROPERTY(TopRight) +HIPPY_VIEW_BORDER_RADIUS_PROPERTY(BottomLeft) +HIPPY_VIEW_BORDER_RADIUS_PROPERTY(BottomRight) - (UIView *)view { - return [[NativeRenderImageView alloc] init]; + return [[HippyImageView alloc] init]; } @end diff --git a/renderer/native/ios/renderer/component/image/NativeRenderImageCache.m b/renderer/native/ios/renderer/component/image/NativeRenderImageCache.m deleted file mode 100644 index 6d30adf5944..00000000000 --- a/renderer/native/ios/renderer/component/image/NativeRenderImageCache.m +++ /dev/null @@ -1,49 +0,0 @@ -/*! - * iOS SDK - * - * Tencent is pleased to support the open source community by making - * Hippy available. - * - * Copyright (C) 2019 THL A29 Limited, a Tencent company. - * All rights reserved. - * - * Licensed 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 "NativeRenderImageCache.h" - -@interface NativeRenderImageCache () -@property (nonatomic, strong) NSCache *imageCache; -@end - -@implementation NativeRenderImageCache - -+ (instancetype)sharedInstance { - static NativeRenderImageCache *cache = nil; - static dispatch_once_t onceToken; - dispatch_once(&onceToken, ^{ - cache = [[[self class] alloc] init]; - }); - return cache; -} - -- (instancetype)init { - self = [super init]; - if (self) { - _imageCache = [[NSCache alloc] init]; - _imageCache.totalCostLimit = 1024 * 1024 * 5; - } - return self; -} - -@end diff --git a/renderer/native/ios/renderer/component/image/NativeRenderImageView+NativeRenderTouchesImplementation.h b/renderer/native/ios/renderer/component/image/NativeRenderImageView+NativeRenderTouchesImplementation.h deleted file mode 100644 index ea89a85fcb3..00000000000 --- a/renderer/native/ios/renderer/component/image/NativeRenderImageView+NativeRenderTouchesImplementation.h +++ /dev/null @@ -1,34 +0,0 @@ -/*! - * iOS SDK - * - * Tencent is pleased to support the open source community by making - * Hippy available. - * - * Copyright (C) 2019 THL A29 Limited, a Tencent company. - * All rights reserved. - * - * Licensed 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 "NativeRenderImageView.h" -#import "NativeRenderTouchesProtocol.h" - -NS_ASSUME_NONNULL_BEGIN - -/** This catagory is used to handle touches event for NativeRenderImageView - */ -@interface NativeRenderImageView (NativeRenderTouchesImplementation) - -@end - -NS_ASSUME_NONNULL_END diff --git a/renderer/native/ios/renderer/component/image/NativeRenderImageView+NativeRenderTouchesImplementation.mm b/renderer/native/ios/renderer/component/image/NativeRenderImageView+NativeRenderTouchesImplementation.mm deleted file mode 100644 index 590ccf41198..00000000000 --- a/renderer/native/ios/renderer/component/image/NativeRenderImageView+NativeRenderTouchesImplementation.mm +++ /dev/null @@ -1,363 +0,0 @@ -/*! - * iOS SDK - * - * Tencent is pleased to support the open source community by making - * Hippy available. - * - * Copyright (C) 2019 THL A29 Limited, a Tencent company. - * All rights reserved. - * - * Licensed 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 "NativeRenderImageView+NativeRenderTouchesImplementation.h" -#import "UIView+DomEvent.h" -#import "UIView+Hippy.h" -#import "UIEvent+TouchResponder.h" -#import "objc/runtime.h" - -@implementation NativeRenderImageView (NativeRenderTouchesImplementation) - -#pragma mark Setter & Getter -- (NSMutableDictionary *)touchesEvents { - NSMutableDictionary *events = objc_getAssociatedObject(self, @selector(touchesEvents)); - if (!events) { - events = [NSMutableDictionary dictionaryWithCapacity:4]; - objc_setAssociatedObject(self, @selector(touchesEvents), events, OBJC_ASSOCIATION_RETAIN_NONATOMIC); - } - return events; -} - -- (UITapGestureRecognizer *)tapGestureRecognizer { - UITapGestureRecognizer *tap = objc_getAssociatedObject(self, @selector(tapGestureRecognizer)); - if (!tap) { - tap = [[UITapGestureRecognizer alloc] initWithTarget:self action:@selector(handleClickEvent)]; - tap.cancelsTouchesInView = NO; - tap.delaysTouchesEnded = NO; - objc_setAssociatedObject(self, @selector(tapGestureRecognizer), tap, OBJC_ASSOCIATION_RETAIN_NONATOMIC); - } - return tap; -} - -- (void)removeTapGestureRecognizerIfExists { - UITapGestureRecognizer *tap = objc_getAssociatedObject(self, @selector(tapGestureRecognizer)); - if (tap) { - objc_setAssociatedObject(self, @selector(tapGestureRecognizer), nil, OBJC_ASSOCIATION_RETAIN_NONATOMIC); - [self removeGestureRecognizer:tap]; - } -} - -- (void)removeLongGestureRecognizerIfExists { - UILongPressGestureRecognizer *longPress = objc_getAssociatedObject(self, @selector(longGestureRecognizer)); - if (longPress) { - objc_setAssociatedObject(self, @selector(longGestureRecognizer), nil, OBJC_ASSOCIATION_RETAIN_NONATOMIC); - [self removeGestureRecognizer:longPress]; - } -} - -- (UILongPressGestureRecognizer *)longGestureRecognizer { - UILongPressGestureRecognizer *longPress = objc_getAssociatedObject(self, @selector(longGestureRecognizer)); - if (!longPress) { - longPress = [[UILongPressGestureRecognizer alloc] initWithTarget:self action:@selector(handleLongClickEvent)]; - longPress.cancelsTouchesInView = NO; - objc_setAssociatedObject(self, @selector(longGestureRecognizer), longPress, OBJC_ASSOCIATION_RETAIN_NONATOMIC); - } - return longPress; -} - -- (void)setPressInEventEnabled:(BOOL)enabled { - objc_setAssociatedObject(self, @selector(pressInEventEnabled), @(enabled), OBJC_ASSOCIATION_RETAIN_NONATOMIC); -} - -- (BOOL)pressInEventEnabled { - NSNumber *enabled = objc_getAssociatedObject(self, @selector(pressInEventEnabled)); - return [enabled boolValue]; -} - -- (NSTimer *)enablePressInTimer { - NSTimer *pressInTimer = [NSTimer scheduledTimerWithTimeInterval:.1f target:self selector:@selector(handlePressInEvent) userInfo:nil repeats:NO]; - objc_setAssociatedObject(self, @selector(enablePressInTimer), pressInTimer, OBJC_ASSOCIATION_RETAIN_NONATOMIC); - return pressInTimer; -} - -- (void)disablePressInTimer { - NSTimer *timer = objc_getAssociatedObject(self, @selector(enablePressInTimer)); - [timer invalidate]; - objc_setAssociatedObject(self, @selector(enablePressInTimer), nil, OBJC_ASSOCIATION_RETAIN_NONATOMIC); -} - -#pragma mark NativeRenderTouchesProtol Implementation -- (void)addViewEvent:(NativeRenderViewEventType)touchEvent eventListener:(OnTouchEventHandler)listener { - self.userInteractionEnabled = YES; - switch (touchEvent) { - case NativeRenderViewEventTypeTouchStart: - case NativeRenderViewEventTypeTouchMove: - case NativeRenderViewEventTypeTouchEnd: - case NativeRenderViewEventTypeTouchCancel: - [self setTouchEventListener:listener forEvent:touchEvent]; - break; - case NativeRenderViewEventTypeClick: - [self addClickEventListener:listener]; - break; - case NativeRenderViewEventTypeLongClick: - [self addLongClickEventListener:listener]; - break; - case NativeRenderViewEventTypePressIn: - [self addPressInEventListener:listener]; - break; - case NativeRenderViewEventTypePressOut: - [self addPressOutEventListener:listener]; - break; - default: - break; - } -} - -- (OnTouchEventHandler)eventListenerForEventType:(NativeRenderViewEventType)eventType { - return [[self touchesEvents] objectForKey:@(eventType)]; -} - -- (void)removeViewEvent:(NativeRenderViewEventType)touchEvent { - [[self touchesEvents] removeObjectForKey:@(touchEvent)]; - if (NativeRenderViewEventTypeClick == touchEvent) { - [self removeTapGestureRecognizerIfExists]; - } - else if (NativeRenderViewEventTypeLongClick == touchEvent) { - [self removeLongGestureRecognizerIfExists]; - } - else if (NativeRenderViewEventTypePressIn == touchEvent) { - if ([self pressInEventEnabled]) { - [self disablePressInTimer]; - [self setPressInEventEnabled:NO]; - } - } -} - -#pragma mark Touch Event Listener Add Methods -- (void)setTouchEventListener:(OnTouchEventHandler)eventListener forEvent:(NativeRenderViewEventType)event { - if (eventListener) { - [[self touchesEvents] setObject:eventListener forKey:@(event)]; - } -} - -- (void)addClickEventListener:(OnTouchEventHandler)eventListener { - [self removeTapGestureRecognizerIfExists]; - UITapGestureRecognizer *tap = [self tapGestureRecognizer]; - [self addGestureRecognizer:tap]; - [[self touchesEvents] setObject:eventListener forKey:@(NativeRenderViewEventTypeClick)]; -} - -- (void)addLongClickEventListener:(OnTouchEventHandler)eventListener { - [self removeLongGestureRecognizerIfExists]; - UILongPressGestureRecognizer *longPress = [self longGestureRecognizer]; - [self addGestureRecognizer:longPress]; - [[self touchesEvents] setObject:eventListener forKey:@(NativeRenderViewEventTypeLongClick)]; -} - -- (void)addPressInEventListener:(OnTouchEventHandler)eventListener { - [self disablePressInTimer]; - [self setPressInEventEnabled:YES]; - [[self touchesEvents] setObject:eventListener forKey:@(NativeRenderViewEventTypePressIn)]; -} - -- (void)addPressOutEventListener:(OnTouchEventHandler)eventListener { - [[self touchesEvents] setObject:eventListener forKey:@(NativeRenderViewEventTypePressOut)]; -} - -#pragma mark Touches Event Handler -- (void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event { - if ([self pressInEventEnabled]) { - [self enablePressInTimer]; - } - if ([self tryToHandleEvent:event forEventType:NativeRenderViewEventTypeTouchStart]) { - OnTouchEventHandler listener = [self eventListenerForEventType:NativeRenderViewEventTypeTouchStart]; - if (listener) { - UITouch *touch = [touches anyObject]; - UIView *rootView = [self NativeRenderRootView]; - CGPoint point = [touch locationInView:rootView]; - const char *name = viewEventNameFromType(NativeRenderViewEventTypeTouchStart); - listener(point, - [self canCapture:name], - [self canBubble:name], - [self canBePreventedByInCapturing:name], - [self canBePreventInBubbling:name]); - } - } - [super touchesBegan:touches withEvent:event]; -} - -- (void)touchesEnded:(NSSet *)touches withEvent:(UIEvent *)event { - if ([self pressInEventEnabled]) { - [self disablePressInTimer]; - } - [self handlePressOutEvent]; - if ([self tryToHandleEvent:event forEventType:NativeRenderViewEventTypeTouchEnd]) { - OnTouchEventHandler listener = [self eventListenerForEventType:NativeRenderViewEventTypeTouchEnd]; - if (listener) { - UITouch *touch = [touches anyObject]; - UIView *rootView = [self NativeRenderRootView]; - CGPoint point = [touch locationInView:rootView]; - const char *name = viewEventNameFromType(NativeRenderViewEventTypeTouchEnd); - listener(point, - [self canCapture:name], - [self canBubble:name], - [self canBePreventedByInCapturing:name], - [self canBePreventInBubbling:name]); - } - } - [super touchesEnded:touches withEvent:event]; -} - -- (void)touchesMoved:(NSSet *)touches withEvent:(UIEvent *)event { - if ([self tryToHandleEvent:event forEventType:NativeRenderViewEventTypeTouchMove]) { - OnTouchEventHandler listener = [self eventListenerForEventType:NativeRenderViewEventTypeTouchMove]; - if (listener) { - [self handlePressOutEvent]; - UITouch *touch = [touches anyObject]; - UIView *rootView = [self NativeRenderRootView]; - CGPoint point = [touch locationInView:rootView]; - const char *name = viewEventNameFromType(NativeRenderViewEventTypeTouchMove); - listener(point, - [self canCapture:name], - [self canBubble:name], - [self canBePreventedByInCapturing:name], - [self canBePreventInBubbling:name]); - } - } - [super touchesMoved:touches withEvent:event]; -} - -- (void)touchesCancelled:(NSSet *)touches withEvent:(UIEvent *)event { - if ([self pressInEventEnabled]) { - [self disablePressInTimer]; - } - [self handlePressOutEvent]; - if ([self tryToHandleEvent:event forEventType:NativeRenderViewEventTypeTouchCancel]) { - OnTouchEventHandler listener = [self eventListenerForEventType:NativeRenderViewEventTypeTouchCancel]; - if (listener) { - UITouch *touch = [touches anyObject]; - UIView *rootView = [self NativeRenderRootView]; - CGPoint point = [touch locationInView:rootView]; - const char *name = viewEventNameFromType(NativeRenderViewEventTypeTouchCancel); - listener(point, - [self canCapture:name], - [self canBubble:name], - [self canBePreventedByInCapturing:name], - [self canBePreventInBubbling:name]); - } - } - [super touchesCancelled:touches withEvent:event]; -} - -- (BOOL)tryToHandleEvent:(UIEvent *)event forEventType:(NativeRenderViewEventType)eventType { - id responder = [event responderForType:eventType]; - if (self == responder) { - return YES; - } - if (nil == responder) { - // assume first responder is self - UIView *responder = nil; - // find out is there any parent view who can handle `eventType` and `onInterceptTouchEvent` is YES - UIView *testingView = self; - while (testingView) { - OnTouchEventHandler handler = [testingView eventListenerForEventType:eventType]; - if (!responder && handler) { - responder = testingView; - } - BOOL onInterceptTouchEvent = testingView.onInterceptTouchEvent; - if (handler && onInterceptTouchEvent) { - responder = testingView; - } - testingView = [testingView parentComponent]; - } - // set first responder for `eventType` - if (responder) { - [event setResponder:responder forType:eventType]; - } - else { - [event setResponder:[NSNull null] forType:eventType]; - } - if (responder == self) { - return YES; - } - } - return NO; -} - -- (void)handleClickEvent { - OnTouchEventHandler listener = [self eventListenerForEventType:NativeRenderViewEventTypeClick]; - if (listener) { - UITapGestureRecognizer *tap = objc_getAssociatedObject(self, @selector(tapGestureRecognizer)); - CGPoint point = [tap locationInView:[self NativeRenderRootView]]; - const char *name = viewEventNameFromType(NativeRenderViewEventTypeClick); - listener(point, - [self canCapture:name], - [self canBubble:name], - [self canBePreventedByInCapturing:name], - [self canBePreventInBubbling:name]); - } -} - -- (void)handleLongClickEvent { - OnTouchEventHandler listener = [self eventListenerForEventType:NativeRenderViewEventTypeLongClick]; - if (listener) { - UILongPressGestureRecognizer *longPress = objc_getAssociatedObject(self, @selector(longGestureRecognizer)); - if (longPress.state == UIGestureRecognizerStateBegan) { - CGPoint point = [longPress locationInView:[self NativeRenderRootView]]; - const char *name = viewEventNameFromType(NativeRenderViewEventTypeLongClick); - listener(point, - [self canCapture:name], - [self canBubble:name], - [self canBePreventedByInCapturing:name], - [self canBePreventInBubbling:name]); - } - } -} - -- (void)handlePressInEvent { - [self disablePressInTimer]; - OnTouchEventHandler listener = [self eventListenerForEventType:NativeRenderViewEventTypePressIn]; - if (listener) { - const char *name = viewEventNameFromType(NativeRenderViewEventTypePressIn); - listener(CGPointZero, - [self canCapture:name], - [self canBubble:name], - [self canBePreventedByInCapturing:name], - [self canBePreventInBubbling:name]); - } -} - -- (void)handlePressOutEvent { - OnTouchEventHandler listener = [self eventListenerForEventType:NativeRenderViewEventTypePressOut]; - if (listener) { - const char *name = viewEventNameFromType(NativeRenderViewEventTypePressOut); - listener(CGPointZero, - [self canCapture:name], - [self canBubble:name], - [self canBePreventedByInCapturing:name], - [self canBePreventInBubbling:name]); - } -} - -- (void)resetAllEvents { - [self removeViewEvent:NativeRenderViewEventTypeTouchStart]; - [self removeViewEvent:NativeRenderViewEventTypeTouchEnd]; - [self removeViewEvent:NativeRenderViewEventTypeTouchMove]; - [self removeViewEvent:NativeRenderViewEventTypeTouchCancel]; - [self removeViewEvent:NativeRenderViewEventTypePressIn]; - [self removeViewEvent:NativeRenderViewEventTypePressOut]; - [self removeViewEvent:NativeRenderViewEventTypeClick]; - [self removeViewEvent:NativeRenderViewEventTypeLongClick]; -} - -@end diff --git a/renderer/native/ios/renderer/component/listview/NativeRenderBaseListView.mm b/renderer/native/ios/renderer/component/listview/NativeRenderBaseListView.mm index 1a4305b1fcc..f64f5aabc98 100644 --- a/renderer/native/ios/renderer/component/listview/NativeRenderBaseListView.mm +++ b/renderer/native/ios/renderer/component/listview/NativeRenderBaseListView.mm @@ -20,7 +20,7 @@ * limitations under the License. */ -#import "HippyAsserts.h" +#import "HippyAssert.h" #import "NativeRenderBaseListView.h" #import "NativeRenderBaseListViewCell.h" #import "NativeRenderBaseListViewDataSource.h" @@ -259,7 +259,7 @@ - (UICollectionReusableView *)collectionView:(UICollectionView *)collectionView forIndexPath:indexPath]; HippyShadowView *headerRenderObject = [self.dataSource headerForSection:section]; if (headerRenderObject && [headerRenderObject isKindOfClass:[HippyShadowView class]]) { - UIView *headerView = [self.renderImpl viewFromRenderViewTag:headerRenderObject.hippyTag onRootTag:headerRenderObject.rootTag]; + UIView *headerView = [self.renderImpl viewForHippyTag:headerRenderObject.hippyTag onRootTag:headerRenderObject.rootTag]; if (!headerView) { headerView = [self.renderImpl createViewRecursivelyFromRenderObject:headerRenderObject]; } @@ -522,6 +522,13 @@ - (void)setHorizontal:(BOOL)horizontal { UICollectionViewFlowLayout *layout = (UICollectionViewFlowLayout *)self.collectionView.collectionViewLayout; layout.scrollDirection = horizontal ? UICollectionViewScrollDirectionHorizontal : UICollectionViewScrollDirectionVertical; [self.collectionView.collectionViewLayout invalidateLayout]; + if (horizontal) { + [self.collectionView setAlwaysBounceHorizontal:YES]; + [self.collectionView setAlwaysBounceVertical:NO]; + } else { + [self.collectionView setAlwaysBounceVertical:YES]; + [self.collectionView setAlwaysBounceHorizontal:NO]; + } } } diff --git a/renderer/native/ios/renderer/component/listview/NativeRenderBaseListViewDataSource.mm b/renderer/native/ios/renderer/component/listview/NativeRenderBaseListViewDataSource.mm index fc2d095ae47..0c983002771 100644 --- a/renderer/native/ios/renderer/component/listview/NativeRenderBaseListViewDataSource.mm +++ b/renderer/native/ios/renderer/component/listview/NativeRenderBaseListViewDataSource.mm @@ -166,7 +166,17 @@ - (void)applyDiff:(NativeRenderBaseListViewDataSource *)another completion(YES); return; } -// NSArray *batchUpdateInvocations = [self cellViewChangeInvocation:another context:context forCollectionView:view]; + + // Attention, we do partial reload only when 1 item change! + // because differential refreshing causes the display event of the cell to not trigger, + // So we limit this to when only one cell is updated + if (context.allChangedItems.count > 1) { + [view reloadData]; + completion(YES); + return; + } + + // The following logic is not perfect and needs to be further refined NSMutableArray *batchUpdate = [NSMutableArray arrayWithCapacity:8]; [self cellDiffFromAnother:another sectionStartAt:0 diff --git a/renderer/native/ios/renderer/component/modal/NativeRenderModalCustomAnimationTransition.h b/renderer/native/ios/renderer/component/modal/HippyModalCustomAnimationTransition.h similarity index 89% rename from renderer/native/ios/renderer/component/modal/NativeRenderModalCustomAnimationTransition.h rename to renderer/native/ios/renderer/component/modal/HippyModalCustomAnimationTransition.h index 2b25cb0b51a..7fdd463b9a9 100644 --- a/renderer/native/ios/renderer/component/modal/NativeRenderModalCustomAnimationTransition.h +++ b/renderer/native/ios/renderer/component/modal/HippyModalCustomAnimationTransition.h @@ -24,6 +24,6 @@ #import #import -@interface NativeRenderModalCustomAnimationTransition : NSObject +@interface HippyModalCustomAnimationTransition : NSObject @property (nonatomic, assign) BOOL isPresent; @end diff --git a/renderer/native/ios/renderer/component/modal/NativeRenderModalCustomAnimationTransition.m b/renderer/native/ios/renderer/component/modal/HippyModalCustomAnimationTransition.m similarity index 94% rename from renderer/native/ios/renderer/component/modal/NativeRenderModalCustomAnimationTransition.m rename to renderer/native/ios/renderer/component/modal/HippyModalCustomAnimationTransition.m index 6542e2d6be7..975ec751d4e 100644 --- a/renderer/native/ios/renderer/component/modal/NativeRenderModalCustomAnimationTransition.m +++ b/renderer/native/ios/renderer/component/modal/HippyModalCustomAnimationTransition.m @@ -20,11 +20,11 @@ * limitations under the License. */ -#import "NativeRenderModalCustomAnimationTransition.h" +#import "HippyModalCustomAnimationTransition.h" #define DURATION .2f -@implementation NativeRenderModalCustomAnimationTransition +@implementation HippyModalCustomAnimationTransition - (NSTimeInterval)transitionDuration:(__unused id)transitionContext { return DURATION; diff --git a/renderer/native/ios/renderer/component/modal/NativeRenderModalCustomPresentationController.h b/renderer/native/ios/renderer/component/modal/HippyModalCustomPresentationController.h similarity index 90% rename from renderer/native/ios/renderer/component/modal/NativeRenderModalCustomPresentationController.h rename to renderer/native/ios/renderer/component/modal/HippyModalCustomPresentationController.h index 3a9779b9d73..ee02e5f805e 100644 --- a/renderer/native/ios/renderer/component/modal/NativeRenderModalCustomPresentationController.h +++ b/renderer/native/ios/renderer/component/modal/HippyModalCustomPresentationController.h @@ -22,6 +22,6 @@ #import -@interface NativeRenderModalCustomPresentationController : UIPresentationController +@interface HippyModalCustomPresentationController : UIPresentationController @end diff --git a/renderer/native/ios/renderer/component/modal/NativeRenderModalCustomPresentationController.m b/renderer/native/ios/renderer/component/modal/HippyModalCustomPresentationController.m similarity index 89% rename from renderer/native/ios/renderer/component/modal/NativeRenderModalCustomPresentationController.m rename to renderer/native/ios/renderer/component/modal/HippyModalCustomPresentationController.m index 935052fdaae..5c6dfb86b22 100644 --- a/renderer/native/ios/renderer/component/modal/NativeRenderModalCustomPresentationController.m +++ b/renderer/native/ios/renderer/component/modal/HippyModalCustomPresentationController.m @@ -20,9 +20,9 @@ * limitations under the License. */ -#import "NativeRenderModalCustomPresentationController.h" +#import "HippyModalCustomPresentationController.h" -@implementation NativeRenderModalCustomPresentationController { +@implementation HippyModalCustomPresentationController { } - (void)presentationTransitionWillBegin { diff --git a/renderer/native/ios/renderer/component/modal/NativeRenderModalHostView.h b/renderer/native/ios/renderer/component/modal/HippyModalHostView.h similarity index 72% rename from renderer/native/ios/renderer/component/modal/NativeRenderModalHostView.h rename to renderer/native/ios/renderer/component/modal/HippyModalHostView.h index dc0035eb065..f24b7a36e15 100644 --- a/renderer/native/ios/renderer/component/modal/NativeRenderModalHostView.h +++ b/renderer/native/ios/renderer/component/modal/HippyModalHostView.h @@ -21,14 +21,15 @@ */ #import - #import "HippyComponent.h" +#import "HippyInvalidating.h" -@class NativeRenderModalHostViewController; - -@protocol NativeRenderModalHostViewInteractor; +@class HippyBridge; +@class HippyTouchHandler; +@class HippyModalHostViewController; +@protocol HippyModalHostViewInteractor; -@interface NativeRenderModalHostView : UIView +@interface HippyModalHostView : UIView @property (nonatomic, copy) NSString *animationType; @property (nonatomic, copy) NSString *primaryKey; @@ -38,14 +39,16 @@ @property (nonatomic, copy) HippyDirectEventBlock onShow; @property (nonatomic, copy) HippyDirectEventBlock onRequestClose; -@property (nonatomic, weak) id delegate; +@property (nonatomic, weak) id delegate; @property (nonatomic, strong) NSArray *supportedOrientations; @property (nonatomic, copy) HippyDirectEventBlock onOrientationChange; @property (nonatomic, strong) NSNumber *hideStatusBar; @property (nonatomic, readonly) BOOL isPresented; -@property (nonatomic, strong) NativeRenderModalHostViewController *modalViewController; +@property (nonatomic, strong) HippyModalHostViewController *modalViewController; +@property (nonatomic, readonly) HippyTouchHandler *touchHandler; +- (instancetype)initWithBridge:(HippyBridge *)bridge NS_DESIGNATED_INITIALIZER; - (void)notifyForBoundsChange:(CGRect)newBounds; @end diff --git a/renderer/native/ios/renderer/component/modal/NativeRenderModalHostView.mm b/renderer/native/ios/renderer/component/modal/HippyModalHostView.m similarity index 73% rename from renderer/native/ios/renderer/component/modal/NativeRenderModalHostView.mm rename to renderer/native/ios/renderer/component/modal/HippyModalHostView.m index 913f034e2e1..d79873080b6 100644 --- a/renderer/native/ios/renderer/component/modal/NativeRenderModalHostView.mm +++ b/renderer/native/ios/renderer/component/modal/HippyModalHostView.m @@ -20,23 +20,35 @@ * limitations under the License. */ -#import "NativeRenderModalHostView.h" -#import "NativeRenderModalHostViewController.h" +#import "HippyModalHostView.h" +#import "HippyModalHostViewController.h" +#import "HippyTouchHandler.h" #import "UIView+Hippy.h" +#import "HippyModalHostViewInteractor.h" +#import "HippyAssert.h" #import "UIView+MountEvent.h" -#import "NativeRenderModalHostViewInteractor.h" -@implementation NativeRenderModalHostView { + +@implementation HippyModalHostView { BOOL _isPresented; - NativeRenderModalHostViewController *_modalViewController; + HippyModalHostViewController *_modalViewController; + UIView *_hippySubview; UIStatusBarStyle originStyle; UIInterfaceOrientation _lastKnownOrientation; } -- (instancetype)initWithFrame:(CGRect)frame { - if ((self = [super initWithFrame:frame])) { - _modalViewController = [NativeRenderModalHostViewController new]; +HIPPY_NOT_IMPLEMENTED(-(instancetype)initWithFrame : (CGRect)frame) +HIPPY_NOT_IMPLEMENTED(-(instancetype)initWithCoder : coder) + +- (instancetype)initWithBridge:(HippyBridge *)bridge { + if ((self = [super initWithFrame:CGRectZero])) { + _modalViewController = [HippyModalHostViewController new]; + UIView *containerView = [UIView new]; + containerView.autoresizingMask = UIViewAutoresizingFlexibleHeight | UIViewAutoresizingFlexibleWidth; + _modalViewController.view = containerView; + _touchHandler = [[HippyTouchHandler alloc] initWithRootView:containerView bridge:bridge]; _isPresented = NO; + __weak __typeof(self) weakSelf = self; _modalViewController.boundsDidChangeBlock = ^(CGRect newBounds) { [weakSelf notifyForBoundsChange:newBounds]; @@ -71,9 +83,21 @@ - (void)notifyForOrientationChange { } - (void)insertHippySubview:(UIView *)subview atIndex:(NSInteger)atIndex { + HippyAssert(_hippySubview == nil, @"Modal view can only have one subview"); [super insertHippySubview:subview atIndex:atIndex]; - [_modalViewController.view insertSubview:subview atIndex:atIndex]; + [subview addGestureRecognizer:_touchHandler]; + subview.autoresizingMask = UIViewAutoresizingFlexibleHeight | UIViewAutoresizingFlexibleWidth; + + [_modalViewController.view insertSubview:subview atIndex:0]; [subview sendAttachedToWindowEvent]; + _hippySubview = subview; +} + +- (void)removeHippySubview:(UIView *)subview { + HippyAssert(subview == _hippySubview, @"Cannot remove view other than modal view"); + [super removeHippySubview:subview]; + [subview removeGestureRecognizer:_touchHandler]; + _hippySubview = nil; } - (void)didUpdateHippySubviews { @@ -105,7 +129,12 @@ - (void)didMoveToWindow { [_delegate presentModalHostView:self withViewController:_modalViewController animated:[self hasAnimationType]]; _isPresented = YES; originStyle = _modalViewController.preferredStatusBarStyle; - UIStatusBarStyle theStyle = self.darkStatusBarText ? UIStatusBarStyleDefault : UIStatusBarStyleLightContent; + UIStatusBarStyle theStyle; + if (@available(iOS 13.0, *)) { + theStyle = self.darkStatusBarText ? UIStatusBarStyleDarkContent : UIStatusBarStyleLightContent; + } else { + theStyle = self.darkStatusBarText ? UIStatusBarStyleDefault : UIStatusBarStyleLightContent; + } [_modalViewController setPreferredStatusBarStyle:theStyle]; } } @@ -118,6 +147,12 @@ - (void)didMoveToSuperview { } } +- (void)invalidate { + dispatch_async(dispatch_get_main_queue(), ^{ + [self dismissModalViewController]; + }); +} + - (BOOL)isTransparent { return _modalViewController.modalPresentationStyle == UIModalPresentationOverFullScreen; } diff --git a/renderer/native/ios/renderer/component/modal/NativeRenderModalHostViewController.h b/renderer/native/ios/renderer/component/modal/HippyModalHostViewController.h similarity index 94% rename from renderer/native/ios/renderer/component/modal/NativeRenderModalHostViewController.h rename to renderer/native/ios/renderer/component/modal/HippyModalHostViewController.h index 4510abd457e..9788547a8a7 100644 --- a/renderer/native/ios/renderer/component/modal/NativeRenderModalHostViewController.h +++ b/renderer/native/ios/renderer/component/modal/HippyModalHostViewController.h @@ -22,7 +22,7 @@ #import -@interface NativeRenderModalHostViewController : UIViewController +@interface HippyModalHostViewController : UIViewController @property (nonatomic, copy) void (^boundsDidChangeBlock)(CGRect newBounds); @property (nonatomic, strong) NSNumber *hideStatusBar; diff --git a/renderer/native/ios/renderer/component/modal/NativeRenderModalHostViewController.mm b/renderer/native/ios/renderer/component/modal/HippyModalHostViewController.mm similarity index 94% rename from renderer/native/ios/renderer/component/modal/NativeRenderModalHostViewController.mm rename to renderer/native/ios/renderer/component/modal/HippyModalHostViewController.mm index 82bd2786a00..12555c26bc7 100644 --- a/renderer/native/ios/renderer/component/modal/NativeRenderModalHostViewController.mm +++ b/renderer/native/ios/renderer/component/modal/HippyModalHostViewController.mm @@ -20,17 +20,17 @@ * limitations under the License. */ -#import "NativeRenderModalHostViewController.h" +#import "HippyModalHostViewController.h" #import "HippyUtils.h" -@interface NativeRenderModalHostViewController () { +@interface HippyModalHostViewController () { CGRect _lastViewFrame; UIStatusBarStyle _preferredStatusBarStyle; } @end -@implementation NativeRenderModalHostViewController +@implementation HippyModalHostViewController - (instancetype)init { self = [super init]; diff --git a/renderer/native/ios/renderer/component/modal/NativeRenderModalHostViewInteractor.h b/renderer/native/ios/renderer/component/modal/HippyModalHostViewInteractor.h similarity index 66% rename from renderer/native/ios/renderer/component/modal/NativeRenderModalHostViewInteractor.h rename to renderer/native/ios/renderer/component/modal/HippyModalHostViewInteractor.h index 73746cc87a1..258de5e3f28 100644 --- a/renderer/native/ios/renderer/component/modal/NativeRenderModalHostViewInteractor.h +++ b/renderer/native/ios/renderer/component/modal/HippyModalHostViewInteractor.h @@ -21,16 +21,16 @@ */ #import -#import "NativeRenderModalHostView.h" -#import "NativeRenderModalHostViewController.h" +#import "HippyModalHostView.h" +#import "HippyModalHostViewController.h" -@protocol NativeRenderModalHostViewInteractor +@protocol HippyModalHostViewInteractor -- (void)presentModalHostView:(NativeRenderModalHostView *)modalHostView - withViewController:(NativeRenderModalHostViewController *)viewController +- (void)presentModalHostView:(HippyModalHostView *)modalHostView + withViewController:(HippyModalHostViewController *)viewController animated:(BOOL)animated; -- (void)dismissModalHostView:(NativeRenderModalHostView *)modalHostView - withViewController:(NativeRenderModalHostViewController *)viewController +- (void)dismissModalHostView:(HippyModalHostView *)modalHostView + withViewController:(HippyModalHostViewController *)viewController animated:(BOOL)animated; @end diff --git a/renderer/native/ios/renderer/component/modal/NativeRenderModalHostViewManager.h b/renderer/native/ios/renderer/component/modal/HippyModalHostViewManager.h similarity index 78% rename from renderer/native/ios/renderer/component/modal/NativeRenderModalHostViewManager.h rename to renderer/native/ios/renderer/component/modal/HippyModalHostViewManager.h index 646b0a68466..80593c59554 100644 --- a/renderer/native/ios/renderer/component/modal/NativeRenderModalHostViewManager.h +++ b/renderer/native/ios/renderer/component/modal/HippyModalHostViewManager.h @@ -21,16 +21,16 @@ */ #import "HippyViewManager.h" -#import "NativeRenderModalHostView.h" +#import "HippyModalHostView.h" -@protocol NativeRenderModalHostViewInteractor; +@protocol HippyModalHostViewInteractor; typedef void (^NativeRenderModalViewInteractionBlock)( UIViewController *reactViewController, UIViewController *viewController, BOOL animated, dispatch_block_t completionBlock); -@interface NativeRenderModalHostViewManager : HippyViewManager +@interface HippyModalHostViewManager : HippyViewManager @property (nonatomic, strong) NSHashTable *hostViews; -@property (nonatomic, strong) id transitioningDelegate; +@property (nonatomic, strong) id transitioningDelegate; @end diff --git a/renderer/native/ios/renderer/component/modal/NativeRenderModalHostViewManager.mm b/renderer/native/ios/renderer/component/modal/HippyModalHostViewManager.mm similarity index 75% rename from renderer/native/ios/renderer/component/modal/NativeRenderModalHostViewManager.mm rename to renderer/native/ios/renderer/component/modal/HippyModalHostViewManager.mm index 222cd126dc3..f459b0267e9 100644 --- a/renderer/native/ios/renderer/component/modal/NativeRenderModalHostViewManager.mm +++ b/renderer/native/ios/renderer/component/modal/HippyModalHostViewManager.mm @@ -21,17 +21,17 @@ */ #import "HippyUtils.h" -#import "NativeRenderModalHostViewController.h" -#import "NativeRenderModalHostViewManager.h" -#import "NativeRenderModalTransitioningDelegate.h" +#import "HippyModalHostViewController.h" +#import "HippyModalHostViewManager.h" +#import "HippyModalTransitioningDelegate.h" #import "HippyShadowView.h" #import "NativeRenderUtils.h" -@interface NativeRenderObjectModalHost : HippyShadowView +@interface HippyModalHostShadowView : HippyShadowView @end -@implementation NativeRenderObjectModalHost +@implementation HippyModalHostShadowView - (void)insertHippySubview:(HippyShadowView *)subview atIndex:(NSInteger)atIndex{ [super insertHippySubview:subview atIndex:atIndex]; @@ -46,7 +46,7 @@ - (void)setDomManager:(std::weak_ptr)domManager { @end -@implementation NativeRenderModalHostViewManager +@implementation HippyModalHostViewManager HIPPY_EXPORT_MODULE(Modal) @@ -61,7 +61,7 @@ @implementation NativeRenderModalHostViewManager HIPPY_EXPORT_VIEW_PROPERTY(hideStatusBar, NSNumber) - (UIView *)view { - NativeRenderModalHostView *view = [[NativeRenderModalHostView alloc] init]; + HippyModalHostView *view = [[HippyModalHostView alloc] initWithBridge:self.bridge]; view.delegate = self.transitioningDelegate; if (!_hostViews) { _hostViews = [NSHashTable weakObjectsHashTable]; @@ -70,15 +70,22 @@ - (UIView *)view { return view; } -- (id)transitioningDelegate { +- (id)transitioningDelegate { if (!_transitioningDelegate) { - _transitioningDelegate = [NativeRenderModalTransitioningDelegate new]; + _transitioningDelegate = [HippyModalTransitioningDelegate new]; } return _transitioningDelegate; } - (HippyShadowView *)hippyShadowView { - return [NativeRenderObjectModalHost new]; + return [HippyModalHostShadowView new]; +} + +- (void)invalidate { + for (HippyModalHostView *hostView in _hostViews) { + [hostView invalidate]; + } + [_hostViews removeAllObjects]; } @end diff --git a/renderer/native/ios/renderer/component/modal/NativeRenderModalTransitioningDelegate.h b/renderer/native/ios/renderer/component/modal/HippyModalTransitioningDelegate.h similarity index 71% rename from renderer/native/ios/renderer/component/modal/NativeRenderModalTransitioningDelegate.h rename to renderer/native/ios/renderer/component/modal/HippyModalTransitioningDelegate.h index 81c78b0a10f..5a0c2d2a2ad 100644 --- a/renderer/native/ios/renderer/component/modal/NativeRenderModalTransitioningDelegate.h +++ b/renderer/native/ios/renderer/component/modal/HippyModalTransitioningDelegate.h @@ -21,16 +21,16 @@ */ #import -#import "NativeRenderModalHostViewInteractor.h" +#import "HippyModalHostViewInteractor.h" -@class NativeRenderModalHostView; -@class NativeRenderModalHostViewController; +@class HippyModalHostView; +@class HippyModalHostViewController; -@protocol NativeRenderModalHostViewInteractor; +@protocol HippyModalHostViewInteractor; typedef void (^NativeRenderModalViewInteractionBlock)( UIViewController *hippyViewController, UIViewController *viewController, BOOL animated, dispatch_block_t completionBlock); -@interface NativeRenderModalTransitioningDelegate : NSObject +@interface HippyModalTransitioningDelegate : NSObject /** * `presentationBlock` and `dismissalBlock` allow you to control how a Modal interacts with your case, * e.g. in case you have a native navigator that has its own way to display a modal. @@ -39,11 +39,11 @@ typedef void (^NativeRenderModalViewInteractionBlock)( @property (nonatomic, strong) NativeRenderModalViewInteractionBlock presentationBlock; @property (nonatomic, strong) NativeRenderModalViewInteractionBlock dismissalBlock; -- (void)presentModalHostView:(NativeRenderModalHostView *)modalHostView - withViewController:(NativeRenderModalHostViewController *)viewController +- (void)presentModalHostView:(HippyModalHostView *)modalHostView + withViewController:(HippyModalHostViewController *)viewController animated:(BOOL)animated; -- (void)dismissModalHostView:(NativeRenderModalHostView *)modalHostView - withViewController:(NativeRenderModalHostViewController *)viewController +- (void)dismissModalHostView:(HippyModalHostView *)modalHostView + withViewController:(HippyModalHostViewController *)viewController animated:(BOOL)animated; @end diff --git a/renderer/native/ios/renderer/component/modal/NativeRenderModalTransitioningDelegate.m b/renderer/native/ios/renderer/component/modal/HippyModalTransitioningDelegate.m similarity index 76% rename from renderer/native/ios/renderer/component/modal/NativeRenderModalTransitioningDelegate.m rename to renderer/native/ios/renderer/component/modal/HippyModalTransitioningDelegate.m index dae2a9dfaa0..0115e7c44c5 100644 --- a/renderer/native/ios/renderer/component/modal/NativeRenderModalTransitioningDelegate.m +++ b/renderer/native/ios/renderer/component/modal/HippyModalTransitioningDelegate.m @@ -20,17 +20,17 @@ * limitations under the License. */ -#import "NativeRenderModalTransitioningDelegate.h" -#import "NativeRenderModalCustomPresentationController.h" -#import "NativeRenderModalCustomAnimationTransition.h" +#import "HippyModalTransitioningDelegate.h" +#import "HippyModalCustomPresentationController.h" +#import "HippyModalCustomAnimationTransition.h" #import "UIView+Hippy.h" -@implementation NativeRenderModalTransitioningDelegate +@implementation HippyModalTransitioningDelegate - (nullable UIPresentationController *)presentationControllerForPresentedViewController:(UIViewController *)presented presentingViewController:(__unused UIViewController *)presenting sourceViewController:(__unused UIViewController *)source NS_AVAILABLE_IOS(8_0) { - NativeRenderModalCustomPresentationController *controller = [[NativeRenderModalCustomPresentationController alloc] initWithPresentedViewController:presented + HippyModalCustomPresentationController *controller = [[HippyModalCustomPresentationController alloc] initWithPresentedViewController:presented presentingViewController:presenting]; return controller; } @@ -38,19 +38,19 @@ - (nullable UIPresentationController *)presentationControllerForPresentedViewCon - (nullable id)animationControllerForPresentedController:(__unused UIViewController *)presented presentingController:(__unused UIViewController *)presenting sourceController:(__unused UIViewController *)source { - NativeRenderModalCustomAnimationTransition *transition = [NativeRenderModalCustomAnimationTransition new]; + HippyModalCustomAnimationTransition *transition = [HippyModalCustomAnimationTransition new]; transition.isPresent = YES; return transition; } - (nullable id)animationControllerForDismissedController:(__unused UIViewController *)dismissed { - NativeRenderModalCustomAnimationTransition *transition = [NativeRenderModalCustomAnimationTransition new]; + HippyModalCustomAnimationTransition *transition = [HippyModalCustomAnimationTransition new]; transition.isPresent = NO; return transition; } -- (void)presentModalHostView:(NativeRenderModalHostView *)modalHostView - withViewController:(NativeRenderModalHostViewController *)viewController +- (void)presentModalHostView:(HippyModalHostView *)modalHostView + withViewController:(HippyModalHostViewController *)viewController animated:(BOOL)animated { dispatch_block_t completionBlock = ^{ if (modalHostView.onShow) { @@ -61,15 +61,15 @@ - (void)presentModalHostView:(NativeRenderModalHostView *)modalHostView _presentationBlock([modalHostView hippyViewController], viewController, animated, completionBlock); } else { if ([modalHostView.hideStatusBar boolValue]) { - viewController.modalPresentationCapturesStatusBarAppearance = YES; viewController.hideStatusBar = [modalHostView hideStatusBar]; } + viewController.modalPresentationCapturesStatusBarAppearance = YES; [[modalHostView hippyViewController] presentViewController:viewController animated:animated completion:completionBlock]; } } -- (void)dismissModalHostView:(NativeRenderModalHostView *)modalHostView - withViewController:(NativeRenderModalHostViewController *)viewController +- (void)dismissModalHostView:(HippyModalHostView *)modalHostView + withViewController:(HippyModalHostViewController *)viewController animated:(BOOL)animated { dispatch_block_t completionBlock = ^{ NSDictionary *userInfo = nil; diff --git a/renderer/native/ios/renderer/component/refreshview/NativeRenderRefreshWrapperViewManager.mm b/renderer/native/ios/renderer/component/refreshview/NativeRenderRefreshWrapperViewManager.mm index 2312a4c95dd..505cc5a06a2 100644 --- a/renderer/native/ios/renderer/component/refreshview/NativeRenderRefreshWrapperViewManager.mm +++ b/renderer/native/ios/renderer/component/refreshview/NativeRenderRefreshWrapperViewManager.mm @@ -22,6 +22,7 @@ #import "NativeRenderRefreshWrapperViewManager.h" #import "NativeRenderRefreshWrapper.h" +#import "HippyComponent.h" #import "HippyUIManager.h" @implementation NativeRenderRefreshWrapperViewManager diff --git a/renderer/native/ios/renderer/component/smartViewPager/NativeRenderSmartViewPagerView.mm b/renderer/native/ios/renderer/component/smartViewPager/NativeRenderSmartViewPagerView.mm index 3df57183838..83cad818a0a 100644 --- a/renderer/native/ios/renderer/component/smartViewPager/NativeRenderSmartViewPagerView.mm +++ b/renderer/native/ios/renderer/component/smartViewPager/NativeRenderSmartViewPagerView.mm @@ -27,7 +27,7 @@ #import "HippyUIManager.h" #import "HippyShadowView.h" #import "NativeRenderSmartViewPagerView.h" -#import "NativeRenderScrollProtocol.h" +#import "HippyScrollProtocol.h" #import "UIView+MountEvent.h" #import "UIView+Render.h" #import "UIView+Hippy.h" diff --git a/renderer/native/ios/renderer/component/textinput/NativeRenderTextField.m b/renderer/native/ios/renderer/component/textinput/NativeRenderTextField.m index f5a28eaf4fb..8a5db6308da 100644 --- a/renderer/native/ios/renderer/component/textinput/NativeRenderTextField.m +++ b/renderer/native/ios/renderer/component/textinput/NativeRenderTextField.m @@ -30,15 +30,37 @@ @implementation NativeRenderUITextField - (void)setKeyboardType:(UIKeyboardType)keyboardType { + if(self.keyboardType == keyboardType){ + return; + } NSString *tempPwdStr = self.text; self.text = @""; if (keyboardType == UIKeyboardTypeTwitter) { self.secureTextEntry = true; } else { self.secureTextEntry = false; - [super setKeyboardType:keyboardType]; } + [super setKeyboardType:keyboardType]; self.text = tempPwdStr; + + if([self isFirstResponder]){ + [self reloadInputViews]; + } +} + +- (UIColor*)caretColor{ + return self.tintColor; +} + +- (void)setCaretColor:(UIColor*)color{ + self.tintColor = color; +} + +- (void)setReturnKeyType:(UIReturnKeyType) returnKeyType{ + [super setReturnKeyType:returnKeyType]; + if([self isFirstResponder]){ + [self reloadInputViews]; + } } - (void)setEditable:(BOOL)editable { diff --git a/renderer/native/ios/renderer/component/textinput/NativeRenderTextView.mm b/renderer/native/ios/renderer/component/textinput/NativeRenderTextView.mm index 1d0e9a321e6..3df6575be09 100644 --- a/renderer/native/ios/renderer/component/textinput/NativeRenderTextView.mm +++ b/renderer/native/ios/renderer/component/textinput/NativeRenderTextView.mm @@ -55,6 +55,21 @@ - (BOOL)resignFirstResponder { return [super resignFirstResponder]; } +- (void)setKeyboardType:(UIKeyboardType)keyboardType { + [super setKeyboardType:keyboardType]; + + if([self isFirstResponder]){ + [self reloadInputViews]; + } +} + +- (UIColor*)caretColor{ + return self.tintColor; +} + +- (void)setCaretColor:(UIColor*)color{ + self.tintColor = color; +} @end @interface NativeRenderTextView () diff --git a/renderer/native/ios/renderer/component/textinput/NativeRenderTextViewManager.mm b/renderer/native/ios/renderer/component/textinput/NativeRenderTextViewManager.mm index 69ea4705707..f2ffb65ec16 100644 --- a/renderer/native/ios/renderer/component/textinput/NativeRenderTextViewManager.mm +++ b/renderer/native/ios/renderer/component/textinput/NativeRenderTextViewManager.mm @@ -29,6 +29,7 @@ #import "NativeRenderTextView.h" #import "NativeRenderTextViewManager.h" #import "HippyBridgeModule.h" +#import "NativeRenderTextSelection.h" @implementation NativeRenderTextViewManager @@ -169,6 +170,7 @@ - (HippyShadowView *)hippyShadowView { HIPPY_EXPORT_VIEW_PROPERTY(selectTextOnFocus, BOOL) HIPPY_EXPORT_VIEW_PROPERTY(selection, NativeRenderTextSelection) HIPPY_EXPORT_VIEW_PROPERTY(text, NSString) +HIPPY_REMAP_VIEW_PROPERTY(caretColor, textView.caretColor, UIColor) HIPPY_CUSTOM_SHADOW_PROPERTY(fontSize, NSNumber, NativeRenderObjectTextView) { view.font = [HippyFont updateFont:view.font withSize:json]; diff --git a/renderer/native/ios/renderer/component/view/HippyView.m b/renderer/native/ios/renderer/component/view/HippyView.m index 039080abc7b..f223f9ae1c8 100644 --- a/renderer/native/ios/renderer/component/view/HippyView.m +++ b/renderer/native/ios/renderer/component/view/HippyView.m @@ -25,7 +25,6 @@ #import "HippyBorderDrawing.h" #import "NativeRenderGradientObject.h" #import "HippyView.h" -#import "UIEvent+TouchResponder.h" #import "UIView+DomEvent.h" #import "UIView+Hippy.h" @@ -344,8 +343,10 @@ - (BOOL)getLayerContentForColor:(UIColor *)color completionBlock:(void (^)(UIIma CGSize imageSize = decodedImage.size; CGSize targetSize = UIEdgeInsetsInsetRect(theFrame, [self bordersAsInsets]).size; CGSize drawSize = makeSizeConstrainWithType(imageSize, targetSize, backgroundSize); - [decodedImage drawInRect:CGRectMake(borderInsets.left + backgroundPositionX, - borderInsets.top + backgroundPositionY, + CGPoint originOffset = CGPointMake((targetSize.width - drawSize.width) / 2.0, + (targetSize.height - drawSize.height) / 2.0); + [decodedImage drawInRect:CGRectMake(borderInsets.left + backgroundPositionX + originOffset.x, + borderInsets.top + backgroundPositionY + originOffset.y, drawSize.width, drawSize.height)]; //draw border diff --git a/renderer/native/ios/renderer/component/view/HippyViewEventProtocol.h b/renderer/native/ios/renderer/component/view/HippyViewEventProtocol.h new file mode 100644 index 00000000000..3fe4d353a9b --- /dev/null +++ b/renderer/native/ios/renderer/component/view/HippyViewEventProtocol.h @@ -0,0 +1,60 @@ +/*! + * iOS SDK + * + * Tencent is pleased to support the open source community by making + * Hippy available. + * + * Copyright (C) 2019 THL A29 Limited, a Tencent company. + * All rights reserved. + * + * Licensed 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 HPViewEventProtocol_h +#define HPViewEventProtocol_h + +#import +#import "NativeRenderTouchesProtocol.h" + +NS_ASSUME_NONNULL_BEGIN + +@protocol HippyViewEventProtocol + +@property (nonatomic, copy) OnTouchEventHandler onClick; +@property (nonatomic, copy) OnTouchEventHandler onLongClick; +@property (nonatomic, copy) OnTouchEventHandler onPressIn; +@property (nonatomic, copy) OnTouchEventHandler onPressOut; + +@property (nonatomic, copy) OnTouchEventHandler onTouchDown; +@property (nonatomic, copy) OnTouchEventHandler onTouchMove; +@property (nonatomic, copy) OnTouchEventHandler onTouchEnd; +@property (nonatomic, copy) OnTouchEventHandler onTouchCancel; + +//@property (nonatomic, copy) HippyDirectEventBlock onAttachedToWindow; +//@property (nonatomic, copy) HippyDirectEventBlock onDetachedFromWindow; + +@property (nonatomic, assign) BOOL onInterceptTouchEvent; +@property (nonatomic, assign) BOOL onInterceptPullUpEvent; + +@end + +@protocol HippyViewTouchHandlerProtocol + +- (BOOL)interceptTouchEvent; + +@end + +NS_ASSUME_NONNULL_END + +#endif /* HPViewEventProtocol_h */ + diff --git a/renderer/native/ios/renderer/component/view/HippyViewManager.h b/renderer/native/ios/renderer/component/view/HippyViewManager.h index d0f90528ad0..3c3dc8d39f6 100644 --- a/renderer/native/ios/renderer/component/view/HippyViewManager.h +++ b/renderer/native/ios/renderer/component/view/HippyViewManager.h @@ -72,6 +72,7 @@ * This handles the simple case, where JS and native property names match. */ #define HIPPY_EXPORT_VIEW_PROPERTY(name, type) \ + typedef type HippyTypeExistCheck##type; \ +(NSArray *)propConfig_##name { \ return @[@ #type]; \ } @@ -80,6 +81,7 @@ * This macro maps a named property to an arbitrary key path in the view. */ #define HIPPY_REMAP_VIEW_PROPERTY(name, keyPath, type) \ + typedef type HippyTypeExistCheck##type; \ +(NSArray *)propConfig_##name { \ return @[@ #type, @ #keyPath]; \ } @@ -97,6 +99,7 @@ * This macro is used to map properties to the shadow view, instead of the view. */ #define HIPPY_EXPORT_SHADOW_PROPERTY(name, type) \ + typedef type HippyTypeExistCheck##type; \ +(NSArray *)propConfigShadow_##name { \ return @[@ #type]; \ } @@ -105,6 +108,7 @@ * This macro maps a named property to an arbitrary key path in the shadow view. */ #define HIPPY_REMAP_SHADOW_PROPERTY(name, keyPath, type) \ + typedef type HippyTypeExistCheck##type; \ +(NSArray *)propConfigShadow_##name { \ return @[@ #type, @ #keyPath]; \ } diff --git a/renderer/native/ios/renderer/component/view/HippyViewManager.mm b/renderer/native/ios/renderer/component/view/HippyViewManager.mm index f9c73dda39a..648a702f301 100644 --- a/renderer/native/ios/renderer/component/view/HippyViewManager.mm +++ b/renderer/native/ios/renderer/component/view/HippyViewManager.mm @@ -20,7 +20,7 @@ * limitations under the License. */ -#import "HippyAsserts.h" +#import "HippyAssert.h" #import "HippyConvert.h" #import "HippyConvert+NativeRender.h" #import "HippyImageProviderProtocol.h" @@ -524,7 +524,7 @@ - (void)loadImageSource:(NSString *)path forView:(HippyView *)view { view.layoutDirection = ConvertDirection(json); } -HIPPY_CUSTOM_SHADOW_PROPERTY(verticalAlign, HippyTextAttachmentVerticalAlign, HippyShadowView) { +HIPPY_CUSTOM_SHADOW_PROPERTY(verticalAlign, NativeRenderTextVerticalAlignType, HippyShadowView) { if (json && [json isKindOfClass:NSString.class]) { view.verticalAlignType = [HippyConvert NativeRenderTextVerticalAlignType:json]; } else if ([json isKindOfClass:NSNumber.class]) { diff --git a/renderer/native/ios/renderer/component/view/NativeRenderTouchesProtocol.h b/renderer/native/ios/renderer/component/view/NativeRenderTouchesProtocol.h index 1fb3887149d..d2def49c4d8 100644 --- a/renderer/native/ios/renderer/component/view/NativeRenderTouchesProtocol.h +++ b/renderer/native/ios/renderer/component/view/NativeRenderTouchesProtocol.h @@ -21,7 +21,6 @@ */ #import -#import "NativeRenderViewEventType.h" NS_ASSUME_NONNULL_BEGIN @@ -37,25 +36,7 @@ typedef void(^OnTouchEventHandler)(CGPoint point, */ @protocol NativeRenderTouchesProtocol -/** - * Add an event for a view - * @param touchEvent event type - * @param listener event handle block - */ -- (void)addViewEvent:(NativeRenderViewEventType)touchEvent eventListener:(OnTouchEventHandler)listener; - -/** - * Get event handle block with event type - * @param eventType event type - * @return event handle block for eventType - */ -- (OnTouchEventHandler)eventListenerForEventType:(NativeRenderViewEventType)eventType; -/** - * Remove event handle block - * @param touchEvent event type - */ -- (void)removeViewEvent:(NativeRenderViewEventType)touchEvent; /** * Indicate if event can be prevented in capturing process @@ -85,8 +66,6 @@ typedef void(^OnTouchEventHandler)(CGPoint point, */ - (BOOL)canBubble:(const char *)name; -- (void)resetAllEvents; - @end NS_ASSUME_NONNULL_END diff --git a/renderer/native/ios/renderer/component/view/NativeRenderTouchesView.m b/renderer/native/ios/renderer/component/view/NativeRenderTouchesView.m index 049074aa0c3..303c2f9d94c 100644 --- a/renderer/native/ios/renderer/component/view/NativeRenderTouchesView.m +++ b/renderer/native/ios/renderer/component/view/NativeRenderTouchesView.m @@ -21,52 +21,14 @@ */ #import "NativeRenderTouchesView.h" -#import "UIEvent+TouchResponder.h" #import "UIView+DomEvent.h" #import "UIView+MountEvent.h" #import "UIView+Hippy.h" #import "objc/runtime.h" -@interface NativeRenderTouchesView () { - NSMutableDictionary *_touchesEvents; - UITapGestureRecognizer *_tapGestureRecognizer; - UILongPressGestureRecognizer *_longGestureRecognizer; - NSTimer *_pressInTimer; - BOOL _pressInEventEnabled; -} - -@end @implementation NativeRenderTouchesView -#pragma mark Life Cycles -- (instancetype)init { - self = [super init]; - if (self) { - [self setDefaultProperties]; - } - return self; -} - -- (instancetype)initWithFrame:(CGRect)frame { - self = [super initWithFrame:frame]; - if (self) { - [self setDefaultProperties]; - } - return self; -} - -- (instancetype)initWithCoder:(NSCoder *)coder { - self = [super initWithCoder:coder]; - if (self) { - [self setDefaultProperties]; - } - return self; -} - -- (void)setDefaultProperties { - self.backgroundColor = [UIColor clearColor]; -} - (void)didMoveToSuperview { [super didMoveToSuperview]; @@ -78,66 +40,6 @@ - (void)didMoveToSuperview { } } -#pragma mark Setter & Getter -- (NSMutableDictionary *)touchesEvents { - if (!_touchesEvents) { - _touchesEvents = [NSMutableDictionary dictionaryWithCapacity:4]; - } - return _touchesEvents; -} - -#pragma mark NativeRenderTouchesProtol Implementation -- (void)addViewEvent:(NativeRenderViewEventType)touchEvent eventListener:(OnTouchEventHandler)listener { - switch (touchEvent) { - case NativeRenderViewEventTypeTouchStart: - case NativeRenderViewEventTypeTouchMove: - case NativeRenderViewEventTypeTouchEnd: - case NativeRenderViewEventTypeTouchCancel: - [self setTouchEventListener:listener forEvent:touchEvent]; - break; - case NativeRenderViewEventTypeClick: - [self addClickEventListener:listener]; - break; - case NativeRenderViewEventTypeLongClick: - [self addLongClickEventListener:listener]; - break; - case NativeRenderViewEventTypePressIn: - [self addPressInEventListener:listener]; - break; - case NativeRenderViewEventTypePressOut: - [self addPressOutEventListener:listener]; - break; - default: - break; - } -} - -- (OnTouchEventHandler)eventListenerForEventType:(NativeRenderViewEventType)eventType { - return [_touchesEvents objectForKey:@(eventType)]; -} - -- (void)removeViewEvent:(NativeRenderViewEventType)touchEvent { - [_touchesEvents removeObjectForKey:@(touchEvent)]; - if (NativeRenderViewEventTypeClick == touchEvent) { - if (_tapGestureRecognizer) { - [self removeGestureRecognizer:_tapGestureRecognizer]; - _tapGestureRecognizer = nil; - } - } - else if (NativeRenderViewEventTypeLongClick == touchEvent) { - if (_longGestureRecognizer) { - [self removeGestureRecognizer:_longGestureRecognizer]; - _longGestureRecognizer = nil; - } - } - else if (NativeRenderViewEventTypePressIn == touchEvent) { - if (_pressInEventEnabled) { - [_pressInTimer invalidate]; - _pressInTimer = nil; - _pressInEventEnabled = NO; - } - } -} - (void)setPointerEvents:(NativeRenderPointerEvents)pointerEvents { _pointerEvents = pointerEvents; @@ -191,238 +93,5 @@ - (UIView *)hitTest:(CGPoint)point withEvent:(UIEvent *)event { } } -#pragma mark Touch Event Listener Add Methods -- (void)setTouchEventListener:(OnTouchEventHandler)eventListener forEvent:(NativeRenderViewEventType)event { - if (eventListener) { - [[self touchesEvents] setObject:eventListener forKey:@(event)]; - } -} - -- (void)addClickEventListener:(OnTouchEventHandler)eventListener { - if (_tapGestureRecognizer) { - return; - } - _tapGestureRecognizer = [[UITapGestureRecognizer alloc] initWithTarget:self action:@selector(handleClickEvent)]; - _tapGestureRecognizer.cancelsTouchesInView = NO; - _tapGestureRecognizer.delaysTouchesEnded = NO; - [self addGestureRecognizer:_tapGestureRecognizer]; - [[self touchesEvents] setObject:eventListener forKey:@(NativeRenderViewEventTypeClick)]; -} - -- (void)addLongClickEventListener:(OnTouchEventHandler)eventListener { - if (_longGestureRecognizer) { - return; - } - _longGestureRecognizer = [[UILongPressGestureRecognizer alloc] initWithTarget:self action:@selector(handleLongClickEvent)]; - _longGestureRecognizer.cancelsTouchesInView = NO; - [self addGestureRecognizer:_longGestureRecognizer]; - [[self touchesEvents] setObject:eventListener forKey:@(NativeRenderViewEventTypeLongClick)]; -} - -- (void)addPressInEventListener:(OnTouchEventHandler)eventListener { - if (_pressInEventEnabled) { - return; - } - if (_pressInTimer) { - [_pressInTimer invalidate]; - } - _pressInEventEnabled = YES; - [[self touchesEvents] setObject:eventListener forKey:@(NativeRenderViewEventTypePressIn)]; -} - -- (void)addPressOutEventListener:(OnTouchEventHandler)eventListener { - [[self touchesEvents] setObject:eventListener forKey:@(NativeRenderViewEventTypePressOut)]; -} - -#pragma mark Touches Event Handler -- (void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event { - if (_pressInEventEnabled) { - _pressInTimer = [NSTimer scheduledTimerWithTimeInterval:.1f target:self selector:@selector(handlePressInEvent) userInfo:nil repeats:NO]; - } - if ([self tryToHandleEvent:event forEventType:NativeRenderViewEventTypeTouchStart]) { - OnTouchEventHandler listener = [self eventListenerForEventType:NativeRenderViewEventTypeTouchStart]; - if (listener) { - UITouch *touch = [touches anyObject]; - UIView *rootView = [self NativeRenderRootView]; - CGPoint point = [touch locationInView:rootView]; - const char *name = viewEventNameFromType(NativeRenderViewEventTypeTouchStart); - listener(point, - [self canCapture:name], - [self canBubble:name], - [self canBePreventedByInCapturing:name], - [self canBePreventInBubbling:name]); - } - } - [super touchesBegan:touches withEvent:event]; -} - -- (void)touchesEnded:(NSSet *)touches withEvent:(UIEvent *)event { - if (_pressInEventEnabled) { - [_pressInTimer invalidate]; - _pressInTimer = nil; - } - [self handlePressOutEvent]; - if ([self tryToHandleEvent:event forEventType:NativeRenderViewEventTypeTouchEnd]) { - OnTouchEventHandler listener = [self eventListenerForEventType:NativeRenderViewEventTypeTouchEnd]; - if (listener) { - UITouch *touch = [touches anyObject]; - UIView *rootView = [self NativeRenderRootView]; - CGPoint point = [touch locationInView:rootView]; - const char *name = viewEventNameFromType(NativeRenderViewEventTypeTouchEnd); - listener(point, - [self canCapture:name], - [self canBubble:name], - [self canBePreventedByInCapturing:name], - [self canBePreventInBubbling:name]); - } - } - [super touchesEnded:touches withEvent:event]; -} - -- (void)touchesMoved:(NSSet *)touches withEvent:(UIEvent *)event { - if ([self tryToHandleEvent:event forEventType:NativeRenderViewEventTypeTouchMove]) { - OnTouchEventHandler listener = [self eventListenerForEventType:NativeRenderViewEventTypeTouchMove]; - if (listener) { - [self handlePressOutEvent]; - UITouch *touch = [touches anyObject]; - UIView *rootView = [self NativeRenderRootView]; - CGPoint point = [touch locationInView:rootView]; - const char *name = viewEventNameFromType(NativeRenderViewEventTypeTouchMove); - listener(point, - [self canCapture:name], - [self canBubble:name], - [self canBePreventedByInCapturing:name], - [self canBePreventInBubbling:name]); - } - } - [super touchesMoved:touches withEvent:event]; -} - -- (void)touchesCancelled:(NSSet *)touches withEvent:(UIEvent *)event { - if (_pressInEventEnabled) { - [_pressInTimer invalidate]; - _pressInTimer = nil; - } - [self handlePressOutEvent]; - if ([self tryToHandleEvent:event forEventType:NativeRenderViewEventTypeTouchCancel]) { - OnTouchEventHandler listener = [self eventListenerForEventType:NativeRenderViewEventTypeTouchCancel]; - if (listener) { - UITouch *touch = [touches anyObject]; - UIView *rootView = [self NativeRenderRootView]; - CGPoint point = [touch locationInView:rootView]; - const char *name = viewEventNameFromType(NativeRenderViewEventTypeTouchCancel); - listener(point, - [self canCapture:name], - [self canBubble:name], - [self canBePreventedByInCapturing:name], - [self canBePreventInBubbling:name]); - } - } - [super touchesCancelled:touches withEvent:event]; -} - -- (BOOL)tryToHandleEvent:(UIEvent *)event forEventType:(NativeRenderViewEventType)eventType { - id responder = [event responderForType:eventType]; - if (self == responder) { - return YES; - } - if (nil == responder) { - // assume first responder is self - UIView *responder = nil; - // find out is there any parent view who can handle `eventType` and `onInterceptTouchEvent` is YES - UIView *testingView = self; - while (testingView) { - OnTouchEventHandler handler = [testingView eventListenerForEventType:eventType]; - if (!responder && handler) { - responder = testingView; - } - BOOL onInterceptTouchEvent = testingView.onInterceptTouchEvent; - if (handler && onInterceptTouchEvent) { - responder = testingView; - } - testingView = [testingView parentComponent]; - } - // set first responder for `eventType` - if (responder) { - [event setResponder:responder forType:eventType]; - } - else { - [event setResponder:[NSNull null] forType:eventType]; - } - if (responder == self) { - return YES; - } - } - return NO; -} - -- (void)handleClickEvent { - OnTouchEventHandler listener = [self eventListenerForEventType:NativeRenderViewEventTypeClick]; - if (listener) { - CGPoint point = [_tapGestureRecognizer locationInView:[self NativeRenderRootView]]; - const char *name = viewEventNameFromType(NativeRenderViewEventTypeClick); - listener(point, - [self canCapture:name], - [self canBubble:name], - [self canBePreventedByInCapturing:name], - [self canBePreventInBubbling:name]); - } -} - -- (void)handleLongClickEvent { - OnTouchEventHandler listener = [self eventListenerForEventType:NativeRenderViewEventTypeLongClick]; - if (listener) { - if (_longGestureRecognizer.state == UIGestureRecognizerStateBegan) { - CGPoint point = [_longGestureRecognizer locationInView:[self NativeRenderRootView]]; - const char *name = viewEventNameFromType(NativeRenderViewEventTypeLongClick); - listener(point, - [self canCapture:name], - [self canBubble:name], - [self canBePreventedByInCapturing:name], - [self canBePreventInBubbling:name]); - } - } -} - -- (void)handlePressInEvent { - [_pressInTimer invalidate]; - _pressInTimer = nil; - OnTouchEventHandler listener = [self eventListenerForEventType:NativeRenderViewEventTypePressIn]; - if (listener) { - const char *name = viewEventNameFromType(NativeRenderViewEventTypePressIn); - listener(CGPointZero, - [self canCapture:name], - [self canBubble:name], - [self canBePreventedByInCapturing:name], - [self canBePreventInBubbling:name]); - } -} - -- (void)handlePressOutEvent { - OnTouchEventHandler listener = [self eventListenerForEventType:NativeRenderViewEventTypePressOut]; - if (listener) { - const char *name = viewEventNameFromType(NativeRenderViewEventTypePressOut); - listener(CGPointZero, - [self canCapture:name], - [self canBubble:name], - [self canBePreventedByInCapturing:name], - [self canBePreventInBubbling:name]); - } -} - -- (void)resetAllEvents { - [_touchesEvents removeAllObjects]; - if (_tapGestureRecognizer) { - [self removeGestureRecognizer:_tapGestureRecognizer]; - } - if (_longGestureRecognizer) { - [self removeGestureRecognizer:_longGestureRecognizer]; - } - if (_pressInEventEnabled || _pressInTimer) { - _pressInEventEnabled = NO; - [_pressInTimer invalidate]; - _pressInTimer = nil; - } -} @end diff --git a/renderer/native/ios/renderer/component/view/NativeRenderViewEventType.h b/renderer/native/ios/renderer/component/view/NativeRenderViewEventType.h deleted file mode 100644 index fc4b7bfa74a..00000000000 --- a/renderer/native/ios/renderer/component/view/NativeRenderViewEventType.h +++ /dev/null @@ -1,50 +0,0 @@ -/*! - * iOS SDK - * - * Tencent is pleased to support the open source community by making - * Hippy available. - * - * Copyright (C) 2019 THL A29 Limited, a Tencent company. - * All rights reserved. - * - * Licensed 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 "HippyDefines.h" - -typedef NS_ENUM(NSInteger, NativeRenderViewEventType) { - //touche event - NativeRenderViewEventTypeTouchStart, - NativeRenderViewEventTypeTouchMove, - NativeRenderViewEventTypeTouchEnd, - NativeRenderViewEventTypeTouchCancel, - - NativeRenderViewEventTypePressIn, - NativeRenderViewEventTypePressOut, - - NativeRenderViewEventLayout, - - //show event - NativeRenderViewEventTypeShow, - NativeRenderViewEventTypeDismiss, - - //click event - NativeRenderViewEventTypeClick, - NativeRenderViewEventTypeLongClick, - - NativeRenderViewEventTypeUnknown = -1, -}; - -HIPPY_EXTERN NativeRenderViewEventType viewEventTypeFromName(const char * _Nullable name); - -HIPPY_EXTERN const char *_Nullable viewEventNameFromType(NativeRenderViewEventType eventType); diff --git a/renderer/native/ios/renderer/component/view/NativeRenderViewEventType.mm b/renderer/native/ios/renderer/component/view/NativeRenderViewEventType.mm deleted file mode 100644 index 6484ff8bfdf..00000000000 --- a/renderer/native/ios/renderer/component/view/NativeRenderViewEventType.mm +++ /dev/null @@ -1,108 +0,0 @@ -/*! - * iOS SDK - * - * Tencent is pleased to support the open source community by making - * Hippy available. - * - * Copyright (C) 2019 THL A29 Limited, a Tencent company. - * All rights reserved. - * - * Licensed 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 "NativeRenderViewEventType.h" - -#include "dom/dom_listener.h" - -NativeRenderViewEventType viewEventTypeFromName(const char * _Nullable name) { - if (!name) { - return NativeRenderViewEventTypeUnknown; - } - NativeRenderViewEventType type = NativeRenderViewEventTypeUnknown; - if (0 == strcmp(hippy::kClickEvent, name)) { - type = NativeRenderViewEventTypeClick; - } - else if (0 == strcmp(hippy::kLongClickEvent, name)) { - type = NativeRenderViewEventTypeLongClick; - } - else if (0 == strcmp(hippy::kTouchStartEvent, name)) { - type = NativeRenderViewEventTypeTouchStart; - } - else if (0 == strcmp(hippy::kTouchMoveEvent, name)) { - type = NativeRenderViewEventTypeTouchMove; - } - else if (0 == strcmp(hippy::kTouchEndEvent, name)) { - type = NativeRenderViewEventTypeTouchEnd; - } - else if (0 == strcmp(hippy::kTouchCancelEvent, name)) { - type = NativeRenderViewEventTypeTouchCancel; - } - else if (0 == strcmp(hippy::kPressIn, name)) { - type = NativeRenderViewEventTypePressIn; - } - else if (0 == strcmp(hippy::kPressOut, name)) { - type = NativeRenderViewEventTypePressOut; - } - else if (0 == strcmp(hippy::kLayoutEvent, name)) { - type = NativeRenderViewEventLayout; - } - else if (0 == strcmp(hippy::kShowEvent, name)) { - type = NativeRenderViewEventTypeShow; - } - else if (0 == strcmp(hippy::kDismissEvent, name)) { - type = NativeRenderViewEventTypeDismiss; - } - return type; -} - -const char * _Nullable viewEventNameFromType(NativeRenderViewEventType eventType) { - const char *name = nullptr; - switch (eventType) { - case NativeRenderViewEventTypeTouchStart: - name = hippy::kTouchStartEvent; - break; - case NativeRenderViewEventTypeTouchMove: - name = hippy::kTouchMoveEvent; - break; - case NativeRenderViewEventTypeTouchEnd: - name = hippy::kTouchEndEvent; - break; - case NativeRenderViewEventTypeTouchCancel: - name = hippy::kTouchCancelEvent; - break; - case NativeRenderViewEventTypePressIn: - name = hippy::kPressIn; - break; - case NativeRenderViewEventTypePressOut: - name = hippy::kPressOut; - break; - case NativeRenderViewEventLayout: - name = hippy::kLayoutEvent; - break; - case NativeRenderViewEventTypeShow: - name = hippy::kShowEvent; - break; - case NativeRenderViewEventTypeDismiss: - name = hippy::kDismissEvent; - break; - case NativeRenderViewEventTypeClick: - name = hippy::kClickEvent; - break; - case NativeRenderViewEventTypeLongClick: - name = hippy::kLongClickEvent; - break; - default: - break; - } - return name; -} diff --git a/renderer/native/ios/renderer/component/view/UIEvent+TouchResponder.m b/renderer/native/ios/renderer/component/view/UIEvent+TouchResponder.m deleted file mode 100644 index af1a68533ab..00000000000 --- a/renderer/native/ios/renderer/component/view/UIEvent+TouchResponder.m +++ /dev/null @@ -1,49 +0,0 @@ -/*! - * iOS SDK - * - * Tencent is pleased to support the open source community by making - * Hippy available. - * - * Copyright (C) 2019 THL A29 Limited, a Tencent company. - * All rights reserved. - * - * Licensed 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 "UIEvent+TouchResponder.h" -#import "objc/runtime.h" - -@implementation UIEvent (TouchResponder) - -- (NSMapTable *)respondersMap { - NSMapTable *map = objc_getAssociatedObject(self, _cmd); - if (!map) { - map = [NSMapTable strongToWeakObjectsMapTable]; - objc_setAssociatedObject(self, _cmd, map, OBJC_ASSOCIATION_RETAIN); - } - return map; -} - -- (void)setResponder:(__weak id)responder forType:(NativeRenderViewEventType)type { - [[self respondersMap] setObject:responder forKey:@(type)]; -} - -- (id)responderForType:(NativeRenderViewEventType)type { - return [[self respondersMap] objectForKey:@(type)]; -} - -- (void)removeAllResponders { - [[self respondersMap] removeAllObjects]; -} - -@end diff --git a/renderer/native/ios/renderer/component/view/UIView+DomEvent.h b/renderer/native/ios/renderer/component/view/UIView+DomEvent.h index 08a638bf7a1..4651140907e 100644 --- a/renderer/native/ios/renderer/component/view/UIView+DomEvent.h +++ b/renderer/native/ios/renderer/component/view/UIView+DomEvent.h @@ -38,7 +38,6 @@ NS_ASSUME_NONNULL_BEGIN @property(nonatomic, assign)BOOL onInterceptTouchEvent; -@property(nonatomic, readonly, copy)NSSet *propertyEventsName; /** * Add custom property event for view @@ -70,7 +69,6 @@ NS_ASSUME_NONNULL_BEGIN */ - (void)didRemovePropertyEvent:(const char *)name; -- (void)removeAllPropertyEvents; @end diff --git a/renderer/native/ios/renderer/component/view/UIView+DomEvent.mm b/renderer/native/ios/renderer/component/view/UIView+DomEvent.mm index a82d9fd54a6..44d3f088791 100644 --- a/renderer/native/ios/renderer/component/view/UIView+DomEvent.mm +++ b/renderer/native/ios/renderer/component/view/UIView+DomEvent.mm @@ -24,24 +24,11 @@ #import #import "UIView+MountEvent.h" #import "UIView+Hippy.h" -#import "UIEvent+TouchResponder.h" #include "dom/dom_listener.h" @implementation UIView(DomEvent) -+ (void)load { - if (self == [UIView self]) { - Method originMethod = class_getInstanceMethod([UIView class], @selector(hitTest:withEvent:)); - Method exchangeMethod = class_getInstanceMethod([UIView class], @selector(hippy_domEvent_hitTest:withEvent:)); - method_exchangeImplementations(originMethod, exchangeMethod); - } -} - -- (UIView *)hippy_domEvent_hitTest:(CGPoint)point withEvent:(UIEvent *)event { - [event removeAllResponders]; - return [self hippy_domEvent_hitTest:point withEvent:event]; -} - (void)setOnInterceptTouchEvent:(BOOL)onInterceptTouchEvent { objc_setAssociatedObject(self, @selector(onInterceptTouchEvent), @(onInterceptTouchEvent), OBJC_ASSOCIATION_RETAIN); @@ -60,10 +47,6 @@ - (BOOL)onInterceptTouchEvent { return names; } -- (NSSet *)propertyEventsName { - return [[self _propertyEventsName] copy]; -} - static SEL SelectorFromCName(const char *name) { if (!name || strlen(name) < 1) { return nil; @@ -141,22 +124,9 @@ - (void)didRemovePropertyEvent:(const char *)name { [[self _propertyEventsName] removeObject:@(name)]; } -- (void)removeAllPropertyEvents { - NSSet *set = [self propertyEventsName]; - for (NSString *name in set) { - [self removePropertyEvent:[name UTF8String]]; - } -} #pragma mark NativeRenderTouchesProtocol Methods -- (void)addViewEvent:(NativeRenderViewEventType)touchEvent eventListener:(OnTouchEventHandler)listener {} - -- (OnTouchEventHandler)eventListenerForEventType:(NativeRenderViewEventType)eventType { - return NULL; -} -- (void)removeViewEvent:(NativeRenderViewEventType)touchEvent { -} - (BOOL)canBePreventedByInCapturing:(const char *)name { return NO; @@ -197,6 +167,4 @@ - (BOOL)canBubble:(const char *)name { return IsGestureEvent(name); } -- (void)resetAllEvents {} - @end diff --git a/renderer/native/ios/renderer/component/view/UIView+Hippy.h b/renderer/native/ios/renderer/component/view/UIView+Hippy.h index 18d19d9a9f9..0f455d6b5a2 100644 --- a/renderer/native/ios/renderer/component/view/UIView+Hippy.h +++ b/renderer/native/ios/renderer/component/view/UIView+Hippy.h @@ -22,10 +22,11 @@ #import #import "HippyComponent.h" +#import "HippyViewEventProtocol.h" @class HippyShadowView; -@interface UIView (Hippy) +@interface UIView (Hippy) /** * reset all hippy subviews diff --git a/renderer/native/ios/renderer/component/view/UIView+Hippy.mm b/renderer/native/ios/renderer/component/view/UIView+Hippy.mm index dfc58a9c1ad..e4d5b08bc22 100644 --- a/renderer/native/ios/renderer/component/view/UIView+Hippy.mm +++ b/renderer/native/ios/renderer/component/view/UIView+Hippy.mm @@ -26,8 +26,51 @@ #import "UIView+MountEvent.h" #import "HippyLog.h" + +#define HippyEventMethod(name, value, type) \ +-(void)set##name : (type)value { \ +objc_setAssociatedObject(self, @selector(value), value, OBJC_ASSOCIATION_COPY_NONATOMIC); \ +} \ +-(type)value { \ +return objc_getAssociatedObject(self, _cmd); \ +} + + @implementation UIView (Hippy) + +#pragma mark - Event Related + +HippyEventMethod(OnClick, onClick, OnTouchEventHandler) +HippyEventMethod(OnPressIn, onPressIn, OnTouchEventHandler) +HippyEventMethod(OnPressOut, onPressOut, OnTouchEventHandler) +HippyEventMethod(OnLongClick, onLongClick, OnTouchEventHandler) +HippyEventMethod(OnTouchDown, onTouchDown, OnTouchEventHandler) +HippyEventMethod(OnTouchMove, onTouchMove, OnTouchEventHandler) +HippyEventMethod(OnTouchCancel, onTouchCancel, OnTouchEventHandler) +HippyEventMethod(OnTouchEnd, onTouchEnd, OnTouchEventHandler) +//HippyEventMethod(OnAttachedToWindow, onAttachedToWindow, HippyDirectEventBlock) +//HippyEventMethod(OnDetachedFromWindow, onDetachedFromWindow, HippyDirectEventBlock) + +- (BOOL)onInterceptTouchEvent { + return [objc_getAssociatedObject(self, _cmd) boolValue]; +} + +- (void)setOnInterceptTouchEvent:(BOOL)onInterceptTouchEvent { + objc_setAssociatedObject(self, @selector(onInterceptTouchEvent), @(onInterceptTouchEvent), OBJC_ASSOCIATION_RETAIN_NONATOMIC); +} + +- (BOOL)onInterceptPullUpEvent { + return [objc_getAssociatedObject(self, _cmd) boolValue]; +} + +- (void)setOnInterceptPullUpEvent:(BOOL)onInterceptPullUpEvent { + objc_setAssociatedObject(self, @selector(onInterceptPullUpEvent), @(onInterceptPullUpEvent), OBJC_ASSOCIATION_RETAIN_NONATOMIC); +} + + +#pragma mark - + - (NSNumber *)hippyTag { return objc_getAssociatedObject(self, _cmd); } @@ -237,23 +280,6 @@ - (void)didUpdateHippySubviews { } - (void)hippySetFrame:(CGRect)frame { - // These frames are in terms of anchorPoint = topLeft, but internally the - // views are anchorPoint = center for easier scale and rotation animations. - // Convert the frame so it works with anchorPoint = center. - CGPoint position = { CGRectGetMidX(frame), CGRectGetMidY(frame) }; - CGRect bounds = { CGPointZero, frame.size }; - - // Avoid crashes due to nan coords - if (isnan(position.x) || isnan(position.y) || isnan(bounds.origin.x) || isnan(bounds.origin.y) || isnan(bounds.size.width) - || isnan(bounds.size.height)) { - HippyLogError( - @"Invalid layout for (%@)%@. position: %@. bounds: %@", self.hippyTag, self, NSStringFromCGPoint(position), NSStringFromCGRect(bounds)); - return; - } - - // self.center = position; - // self.bounds = bounds; - self.frame = frame; } diff --git a/renderer/native/ios/renderer/component/waterfalllist/NativeRenderObjectWaterfall.h b/renderer/native/ios/renderer/component/waterfalllist/NativeRenderObjectWaterfall.h index a37c728a081..08b1eb9014c 100644 --- a/renderer/native/ios/renderer/component/waterfalllist/NativeRenderObjectWaterfall.h +++ b/renderer/native/ios/renderer/component/waterfalllist/NativeRenderObjectWaterfall.h @@ -32,8 +32,15 @@ NS_ASSUME_NONNULL_BEGIN - (NSSet<__kindof HippyShadowView *> *)deletedItems; - (NSHashTable<__kindof HippyShadowView *> *)movedItems; +/// Clear all items recorded. - (void)clear; +/// Whether has changed item. +- (BOOL)hasChanges; + +/// Get all chaned items. +- (NSSet *)allChangedItems; + @end @interface NativeRenderObjectWaterfall : HippyShadowView diff --git a/renderer/native/ios/renderer/component/waterfalllist/NativeRenderObjectWaterfall.mm b/renderer/native/ios/renderer/component/waterfalllist/NativeRenderObjectWaterfall.mm index 9008fe669d1..727ca5c3ebc 100644 --- a/renderer/native/ios/renderer/component/waterfalllist/NativeRenderObjectWaterfall.mm +++ b/renderer/native/ios/renderer/component/waterfalllist/NativeRenderObjectWaterfall.mm @@ -22,7 +22,7 @@ #import "NativeRenderObjectWaterfall.h" #import "NativeRenderWaterfallView.h" -#import "HippyAsserts.h" +#import "HippyAssert.h" @interface WaterfallItemChangeContext () { NSMutableSet *_deletedItems; @@ -73,9 +73,8 @@ - (void)appendMovedItem:(__kindof HippyShadowView *)objectView { } - (void)appendFrameChangedItem:(__kindof HippyShadowView *)objectView { - if (![_addedItems containsObject:objectView]) { - [_frameChangedItems addObject:objectView]; - } + // _frameChangedItems may be also in other addedItems/movedItems + [_frameChangedItems addObject:objectView]; } - (NSSet<__kindof HippyShadowView *> *)deletedItems { @@ -94,6 +93,20 @@ - (void)appendFrameChangedItem:(__kindof HippyShadowView *)objectView { return [_frameChangedItems copy]; } +- (BOOL)hasChanges { + return _addedItems.count != 0 || _deletedItems.count != 0 || + _movedItems.count != 0 || _frameChangedItems.count != 0; +} + +- (NSSet *)allChangedItems { + NSMutableSet *allChanges = [NSMutableSet set]; + [allChanges addObjectsFromArray:_addedItems.allObjects]; + [allChanges addObjectsFromArray:_deletedItems.allObjects]; + [allChanges addObjectsFromArray:_movedItems.allObjects]; + [allChanges addObjectsFromArray:_frameChangedItems.allObjects]; + return allChanges; +} + - (void)clear { [_deletedItems removeAllObjects]; [_addedItems removeAllObjects]; @@ -162,12 +175,13 @@ - (void)itemFrameChanged:(__kindof NativeRenderObjectWaterfallItem *)item { } - (void)amendLayoutBeforeMount:(NSMutableSet *)blocks { - if ([self isPropagationDirty:NativeRenderUpdateLifecycleLayoutDirtied]) { - __weak NativeRenderObjectWaterfall *weakSelf = self; + if ([self isPropagationDirty:NativeRenderUpdateLifecycleLayoutDirtied] && + _itemChangeContext.hasChanges) { WaterfallItemChangeContext *context = [_itemChangeContext copy]; NSArray *dataSource = [self.subcomponents copy]; + __weak __typeof(self)weakSelf = self; NativeRenderApplierBlock block = ^void(NSDictionary *viewRegistry) { - NativeRenderObjectWaterfall *strongSelf = weakSelf; + __strong __typeof(weakSelf)strongSelf = weakSelf; if (!strongSelf) { return; } @@ -175,14 +189,14 @@ - (void)amendLayoutBeforeMount:(NSMutableSet *)blocks HippyAssert([view isKindOfClass:[NativeRenderWaterfallView class]], @"view must be kind of NativeRenderWaterfallView"); if ([view isKindOfClass:[NativeRenderWaterfallView class]]) { view.dirtyContent = YES; - view.changeContext = [context copy]; + view.changeContext = context; [view pushDataSource:dataSource]; } }; [blocks addObject:block]; + [_itemChangeContext clear]; } [super amendLayoutBeforeMount:blocks]; - [_itemChangeContext clear]; } @end diff --git a/renderer/native/ios/renderer/component/waterfalllist/NativeRenderWaterfallView.h b/renderer/native/ios/renderer/component/waterfalllist/NativeRenderWaterfallView.h index 5a261431397..26d05498e5b 100644 --- a/renderer/native/ios/renderer/component/waterfalllist/NativeRenderWaterfallView.h +++ b/renderer/native/ios/renderer/component/waterfalllist/NativeRenderWaterfallView.h @@ -25,7 +25,7 @@ #import "NativeRenderCollectionViewWaterfallLayout.h" #import "HippyComponent.h" #import "HippyScrollableProtocol.h" -#import "NativeRenderScrollProtocol.h" +#import "HippyScrollProtocol.h" #import "NativeRenderTouchesView.h" NS_ASSUME_NONNULL_BEGIN @@ -42,7 +42,7 @@ typedef NS_ENUM(NSInteger, NativeRenderScrollState) { * NativeRenderWaterfallView is a waterfall component, internal implementation is UICollectionView */ @interface NativeRenderWaterfallView : NativeRenderTouchesView { + NativeRenderCollectionViewDelegateWaterfallLayout, HippyScrollableProtocol, HippyScrollProtocol> { @protected NativeRenderWaterfallViewDataSource *_dataSource; NativeRenderWaterfallViewDataSource *_previousDataSource; diff --git a/renderer/native/ios/renderer/component/waterfalllist/NativeRenderWaterfallView.mm b/renderer/native/ios/renderer/component/waterfalllist/NativeRenderWaterfallView.mm index 5e93bd257e6..f0875ee1a05 100644 --- a/renderer/native/ios/renderer/component/waterfalllist/NativeRenderWaterfallView.mm +++ b/renderer/native/ios/renderer/component/waterfalllist/NativeRenderWaterfallView.mm @@ -67,7 +67,7 @@ - (instancetype)initWithFrame:(CGRect)frame { _scrollEventThrottle = 100.f; _weakItemMap = [NSMapTable strongToWeakObjectsMapTable]; _cachedItems = [NSMutableDictionary dictionaryWithCapacity:32]; - _dataSourcePool = [NSMutableArray arrayWithCapacity:8]; + _dataSourcePool = [NSMutableArray array]; _dataSourceSem = dispatch_semaphore_create(1); [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(didReceiveMemoryWarning) name:UIApplicationDidReceiveMemoryWarningNotification object:nil]; [self initCollectionView]; @@ -85,6 +85,7 @@ - (void)initCollectionView { collectionView.dataSource = self; collectionView.delegate = self; collectionView.layoutDelegate = self; + collectionView.alwaysBounceVertical = YES; collectionView.backgroundColor = [UIColor clearColor]; _collectionView = collectionView; [self registerCells]; diff --git a/renderer/native/ios/renderer/component/waterfalllist/NativeRenderWaterfallViewDataSource.mm b/renderer/native/ios/renderer/component/waterfalllist/NativeRenderWaterfallViewDataSource.mm index 99dbc3a6745..676fac99b92 100644 --- a/renderer/native/ios/renderer/component/waterfalllist/NativeRenderWaterfallViewDataSource.mm +++ b/renderer/native/ios/renderer/component/waterfalllist/NativeRenderWaterfallViewDataSource.mm @@ -22,7 +22,7 @@ #import -#import "HippyAsserts.h" +#import "HippyAssert.h" #import "NativeRenderWaterfallViewDataSource.h" #import "HippyShadowView.h" #import "NativeRenderObjectWaterfall.h" diff --git a/renderer/native/ios/renderer/component/webview/NativeRenderSimpleWebView.h b/renderer/native/ios/renderer/component/webview/NativeRenderSimpleWebView.h index 4229d821227..e11311be301 100644 --- a/renderer/native/ios/renderer/component/webview/NativeRenderSimpleWebView.h +++ b/renderer/native/ios/renderer/component/webview/NativeRenderSimpleWebView.h @@ -26,6 +26,7 @@ NS_ASSUME_NONNULL_BEGIN @interface NativeRenderSimpleWebView : WKWebView +@property (nonatomic, strong) NSString *userAgent; @property (nonatomic, strong) NSString *url; @property (nonatomic, strong) NSDictionary *source; @property (nonatomic, copy) HippyDirectEventBlock onLoadStart; diff --git a/renderer/native/ios/renderer/component/webview/NativeRenderSimpleWebView.m b/renderer/native/ios/renderer/component/webview/NativeRenderSimpleWebView.m index 25f864bdff9..9a5fb108822 100644 --- a/renderer/native/ios/renderer/component/webview/NativeRenderSimpleWebView.m +++ b/renderer/native/ios/renderer/component/webview/NativeRenderSimpleWebView.m @@ -38,17 +38,35 @@ - (void)setSource:(NSDictionary *)source { _source = source; if (source && [source[@"uri"] isKindOfClass:[NSString class]]) { NSString *urlString = source[@"uri"]; - [self loadUrl:urlString]; + NSString *method = source[@"method"]; + + // Wait for other properties to be updated + dispatch_async(dispatch_get_main_queue(), ^{ + [self loadUrl:urlString withMethod:method]; + }); } } -- (void)loadUrl:(NSString *)urlString { +- (void)loadUrl:(NSString *)urlString withMethod:(NSString*)method { _url = urlString; NSURL *url = HippyURLWithString(urlString, NULL); if (!url) { return; } - NSURLRequest *request = [NSURLRequest requestWithURL:url]; + + NSMutableURLRequest* request = [NSMutableURLRequest requestWithURL:url]; + method = [method uppercaseString]; + if([method isEqualToString:@"GET"]){ + request.HTTPMethod = @"GET"; + }else if ([method isEqualToString:@"POST"]){ + request.HTTPMethod = @"POST"; + }else{ + // System default is 'GET' no need to be specified explicitly + } + NSString* ua = self.userAgent; + if(ua){ + self.customUserAgent = ua; + } [self loadRequest:request]; } diff --git a/renderer/native/ios/renderer/component/webview/NativeRenderSimpleWebViewManager.mm b/renderer/native/ios/renderer/component/webview/NativeRenderSimpleWebViewManager.mm index b9ea67249f9..3149de34a8b 100644 --- a/renderer/native/ios/renderer/component/webview/NativeRenderSimpleWebViewManager.mm +++ b/renderer/native/ios/renderer/component/webview/NativeRenderSimpleWebViewManager.mm @@ -26,6 +26,7 @@ @implementation NativeRenderSimpleWebViewManager HIPPY_EXPORT_MODULE(WebView) +HIPPY_EXPORT_VIEW_PROPERTY(userAgent, NSString) HIPPY_EXPORT_VIEW_PROPERTY(source, NSDictionary) HIPPY_EXPORT_VIEW_PROPERTY(onLoadStart, HippyDirectEventBlock) HIPPY_EXPORT_VIEW_PROPERTY(onLoadEnd, HippyDirectEventBlock) diff --git a/renderer/native/ios/renderer/component/image/NativeRenderImageCache.h b/renderer/native/ios/renderer/touch_handler/HippyCustomTouchHandlerProtocol.h similarity index 56% rename from renderer/native/ios/renderer/component/image/NativeRenderImageCache.h rename to renderer/native/ios/renderer/touch_handler/HippyCustomTouchHandlerProtocol.h index 5e4726efc4c..f304125d65f 100644 --- a/renderer/native/ios/renderer/component/image/NativeRenderImageCache.h +++ b/renderer/native/ios/renderer/touch_handler/HippyCustomTouchHandlerProtocol.h @@ -20,14 +20,27 @@ * limitations under the License. */ +#import "HippyBridgeModule.h" #import -NS_ASSUME_NONNULL_BEGIN +/** + * used for custom touche handler + */ +@protocol HippyCustomTouchHandlerProtocol -@interface NativeRenderImageCache : NSObject +/** + * if The following methods return YES, HippyTouchHandler will return, + * see implements in HippyTouchHandler.m + */ +@optional +- (BOOL)customTouchesBegan:(NSSet *)touches withEvent:(UIEvent *)event; -+ (instancetype)sharedInstance; +- (BOOL)customTouchesMoved:(NSSet *)touches withEvent:(UIEvent *)event; -@end +- (BOOL)customTouchesEnded:(NSSet *)touches withEvent:(UIEvent *)event; -NS_ASSUME_NONNULL_END +- (BOOL)customTouchesCancelled:(NSSet *)touches withEvent:(UIEvent *)event; + +- (BOOL)customReset; + +@end diff --git a/renderer/native/ios/renderer/component/view/UIEvent+TouchResponder.h b/renderer/native/ios/renderer/touch_handler/HippyTouchHandler.h similarity index 60% rename from renderer/native/ios/renderer/component/view/UIEvent+TouchResponder.h rename to renderer/native/ios/renderer/touch_handler/HippyTouchHandler.h index c869b7780ae..20f5006ef50 100644 --- a/renderer/native/ios/renderer/component/view/UIEvent+TouchResponder.h +++ b/renderer/native/ios/renderer/touch_handler/HippyTouchHandler.h @@ -21,19 +21,19 @@ */ #import -#import "NativeRenderViewEventType.h" +#import "HippyBridge.h" -NS_ASSUME_NONNULL_BEGIN +/// Handles all gestures in Hippy +@interface HippyTouchHandler : UIGestureRecognizer -@interface UIEvent (TouchResponder) +/// Init method +/// - Parameters: +/// - view: rootView for touch, currently it can be HippyRootView or HippyModalHostView +/// - bridge: HippyBridge +- (instancetype)initWithRootView:(UIView *)view bridge:(HippyBridge *)bridge NS_DESIGNATED_INITIALIZER; +- (instancetype)initWithTarget:(id)target action:(SEL)action NS_UNAVAILABLE; -- (void)setResponder:(__weak id)responder forType:(NativeRenderViewEventType)type; - -- (id)responderForType:(NativeRenderViewEventType)type; - -- (void)removeAllResponders; +/// Force cancel touch +- (void)cancelTouch; @end - -NS_ASSUME_NONNULL_END - diff --git a/renderer/native/ios/renderer/touch_handler/HippyTouchHandler.mm b/renderer/native/ios/renderer/touch_handler/HippyTouchHandler.mm new file mode 100644 index 00000000000..f8373a5bf2f --- /dev/null +++ b/renderer/native/ios/renderer/touch_handler/HippyTouchHandler.mm @@ -0,0 +1,814 @@ +/*! + * iOS SDK + * + * Tencent is pleased to support the open source community by making + * Hippy available. + * + * Copyright (C) 2019 THL A29 Limited, a Tencent company. + * All rights reserved. + * + * Licensed 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 "HippyTouchHandler.h" +#import "UIView+Hippy.h" +#import "HippyScrollProtocol.h" +#import "HippyUIManager.h" + +#include "dom/dom_listener.h" + +typedef void (^ViewBlock)(UIView *view, BOOL *stop); + +@interface UIView (HippyViewExtensions) +- (void)hippyLoopViewHierarchy:(ViewBlock)block; +- (void)hippyLoopSuperViewHierarchy:(ViewBlock)block; +- (UIView *)nextResponseViewAtPoint:(CGPoint)point; +@end + +@implementation UIView (HippyViewExtensions) + +- (void)hippyLoopViewHierarchy:(ViewBlock)block { + BOOL stop = NO; + if (block) { + block(self, &stop); + } + if (!stop) { + for (UIView *subview in self.subviews) { + [subview hippyLoopViewHierarchy:block]; + } + } +} + +- (void)hippyLoopSuperViewHierarchy:(ViewBlock)block { + BOOL stop = NO; + if (block) { + block(self, &stop); + } + if (!stop) { + [self.superview hippyLoopSuperViewHierarchy:block]; + } +} + +static bool isPointInsideView(UIView *view, CGPoint point) { + // use presentationLayer to adapt the view with animation + CALayer *presentationLayer = view.layer.presentationLayer; + if (presentationLayer) { + CGPoint layerPoint = [presentationLayer convertPoint:point fromLayer:view.layer]; + return [presentationLayer containsPoint:layerPoint]; + } + return false; +} + +- (UIView *)nextResponseViewAtPoint:(CGPoint)point { + UIView *superView = [self superview]; + if (superView && self.hippyTag) { + NSArray *subviews = [superView subviews]; + NSUInteger index = [subviews indexOfObject:self]; + if (0 != index) { + for (NSInteger i = index - 1; i >= 0; i--) { + UIView *siblingView = subviews[i]; + CGPoint pointInsiblingView = [self convertPoint:point toView:siblingView]; + BOOL pointInside = isPointInsideView(siblingView, pointInsiblingView); + if (pointInside) { + UIView *hitTestView = [siblingView hitTest:pointInsiblingView withEvent:nil]; + return hitTestView ? hitTestView : siblingView; + } + } + } + } + return superView; +} + +@end + + +@interface HippyTouchHandler () + +/** + * Indicate if event can be prevented in capturing process + * @param name event name in std::string type + * @return YES if event can be prevented in capturing process + */ +- (BOOL)canBePreventedByInCapturing:(const char *)name; + +/** + * Indicate if event can be prevented in bubbling process + * @param name event name in std::string type + * @return YES if event can be prevented in bubbling process + */ +- (BOOL)canBePreventInBubbling:(const char *)name; + +/** + * Indicate if event can capture + * @param name event name + * @return YES if event can capture + */ +- (BOOL)canCapture:(const char *)name; + +/** + * Indicate if event can bubble + * @param name event name + * @return YES if event can bubble + */ +- (BOOL)canBubble:(const char *)name; + +@end + + +@implementation HippyTouchHandler { + NSMutableArray *_moveTouches; + NSMutableArray *_moveViews; + + __weak UIView *_onPressInView; + __weak UIView *_onClickView; + __weak UIView *_onLongClickView; + + NSTimer *_toucheBeginTimer; + NSTimer *_touchLongTimer; + BOOL _bPressIn; + BOOL _bLongClick; + + __weak UIView *_rootView; + __weak UIView *_touchBeganView; + + CGPoint _startPoint; + HippyBridge *_bridge; + + NSHashTable *_onInterceptTouchEventView; + NSHashTable *_onInterceptPullUpEventView; +} + +- (instancetype)initWithRootView:(UIView *)view bridge:(HippyBridge *)bridge { + if (self = [super initWithTarget:nil action:NULL]) { + _moveTouches = [NSMutableArray new]; + _moveViews = [NSMutableArray new]; + _startPoint = CGPointZero; + _rootView = view; + self.delegate = self; + self.cancelsTouchesInView = NO; + _onInterceptTouchEventView = [NSHashTable weakObjectsHashTable]; + _onInterceptPullUpEventView = [NSHashTable weakObjectsHashTable]; + + _bridge = bridge; + } + return self; +} + +- (void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event { + [super touchesBegan:touches withEvent:event]; + if ([_bridge.customTouchHandler respondsToSelector:@selector(customTouchesBegan:withEvent:)]) { + BOOL shouldRecursive = [_bridge.customTouchHandler customTouchesBegan:touches withEvent:event]; + if (!shouldRecursive) { + return; + } + } + + UITouch *touch = [touches anyObject]; + _startPoint = [touch locationInView:touch.view]; + { + UIView *touchView = [touch view]; + CGPoint locationPoint = [touch locationInView:touchView]; + touchView = touchView?:[self.view.window hitTest:locationPoint withEvent:event]; + _touchBeganView = touchView; + NSDictionary *result = [self responseViewForAction:@[@"onPressIn", @"onTouchDown", @"onClick", @"onLongClick"] inView:touchView + atPoint:locationPoint]; + + UIView *view = result[@"onTouchDown"][@"view"]; + UIView *clickView = result[@"onClick"][@"view"]; + if (view) { + NSInteger index = [result[@"onTouchDown"][@"index"] integerValue]; + NSInteger clickIndex = NSNotFound; + if (clickView) { + clickIndex = [result[@"onClick"][@"index"] integerValue]; + } + + if (clickView == nil || (index <= clickIndex && clickIndex != NSNotFound)) { + CGPoint point = [touch locationInView:view]; + point = [view convertPoint:point toView:_rootView]; + if (view.onTouchDown) { + if ([self checkViewBelongToTouchHandler:view]) { +// view.onTouchDown(@{ @"page_x": @(point.x), @"page_y": @(point.y) }); + const char *name = hippy::kTouchStartEvent; + view.onTouchDown(point, + [self canCapture:name], + [self canBubble:name], + [self canBePreventedByInCapturing:name], + [self canBePreventInBubbling:name]); + } + } + } + } + + if (result[@"onPressIn"][@"view"]) { + _onPressInView = result[@"onPressIn"][@"view"]; + [self clearTimer]; + _toucheBeginTimer = [NSTimer timerWithTimeInterval:0.1 target:self selector:@selector(scheduleTimer:) userInfo:nil repeats:NO]; + [[NSRunLoop mainRunLoop] addTimer:_toucheBeginTimer forMode:NSDefaultRunLoopMode]; + } + + _onClickView = clickView; + + if (result[@"onLongClick"][@"view"]) { + _onLongClickView = result[@"onLongClick"][@"view"]; + [self clearLongClickTimer]; + _touchLongTimer = [NSTimer timerWithTimeInterval:.6f target:self selector:@selector(longClickTimer:) userInfo:nil repeats:NO]; + [[NSRunLoop mainRunLoop] addTimer:_touchLongTimer forMode:NSDefaultRunLoopMode]; + } + } + + if (self.state == UIGestureRecognizerStatePossible) { + self.state = UIGestureRecognizerStateBegan; + } else if (self.state == UIGestureRecognizerStateBegan) { + self.state = UIGestureRecognizerStateChanged; + } +} + +- (void)touchesEnded:(NSSet *)touches withEvent:(UIEvent *)event { + [super touchesEnded:touches withEvent:event]; + if ([_bridge.customTouchHandler respondsToSelector:@selector(customTouchesEnded:withEvent:)]) { + BOOL shouldRecursive = [_bridge.customTouchHandler customTouchesEnded:touches withEvent:event]; + if (!shouldRecursive) { + return; + } + } + + UITouch *touch = [touches anyObject]; + { + UIView *touchView = [touch view]?:_touchBeganView; + CGPoint locationPoint = [touch locationInView:touchView]; + touchView = touchView?:[self.view.window hitTest:locationPoint withEvent:event]; + NSDictionary *result = [self responseViewForAction:@[@"onTouchEnd", @"onPressOut", @"onClick"] inView:touchView + atPoint:locationPoint]; + + UIView *view = result[@"onTouchEnd"][@"view"]; + UIView *clickView = result[@"onClick"][@"view"]; + if (view) { + NSInteger index = [result[@"onTouchEnd"][@"index"] integerValue]; + NSInteger clickIndex = NSNotFound; + if (clickView) { + clickIndex = [result[@"onClick"][@"index"] integerValue]; + } + + if (clickView == nil || (index <= clickIndex && clickIndex != NSNotFound)) { + CGPoint point = [touch locationInView:view]; + point = [view convertPoint:point toView:_rootView]; + if (view.onTouchEnd) { + if ([self checkViewBelongToTouchHandler:view]) { +// view.onTouchEnd(@{ @"page_x": @(point.x), @"page_y": @(point.y) }); + const char *name = hippy::kTouchEndEvent; + view.onTouchEnd(point, + [self canCapture:name], + [self canBubble:name], + [self canBePreventedByInCapturing:name], + [self canBePreventInBubbling:name]); + } + } + } + } else { + if (_moveViews.count > 0 && _moveViews[0] && _moveViews[0][@"onTouchMove"]) { + NSDictionary *bundle = _moveViews[0][@"onTouchMove"]; + if (bundle && bundle[@"view"]) { + UIView *theView = bundle[@"view"]; + CGPoint point = [touch locationInView:theView]; + point = [theView convertPoint:point toView:_rootView]; + if (theView.onTouchEnd) { + if ([self checkViewBelongToTouchHandler:theView]) { +// theView.onTouchEnd(@{ @"page_x": @(point.x), @"page_y": @(point.y) }); + const char *name = hippy::kTouchEndEvent; + theView.onTouchEnd(point, + [self canCapture:name], + [self canBubble:name], + [self canBePreventedByInCapturing:name], + [self canBePreventInBubbling:name]); + } + } + } + } + } + + if (result[@"onPressOut"][@"view"]) { + UIView *pressOutView = result[@"onPressOut"][@"view"]; + if (pressOutView == _onPressInView && pressOutView.onPressOut) { + if ([self checkViewBelongToTouchHandler:pressOutView]) { + const char *name = hippy::kPressOut; + pressOutView.onPressOut(CGPointZero, + [self canCapture:name], + [self canBubble:name], + [self canBePreventedByInCapturing:name], + [self canBePreventInBubbling:name]); + _onPressInView = nil; + _bPressIn = NO; + } + } + } + + if (clickView && clickView == _onClickView) { + if (!_bLongClick && clickView.onClick) { + if ([self checkViewBelongToTouchHandler:clickView]) { + const char *name = hippy::kClickEvent; + clickView.onClick(CGPointZero, + [self canCapture:name], + [self canBubble:name], + [self canBePreventedByInCapturing:name], + [self canBePreventInBubbling:name]); + } + } + [self clearTimer]; + [self clearLongClickTimer]; + _bPressIn = NO; + } + } + + self.state = UIGestureRecognizerStateEnded; + [_moveViews removeAllObjects]; + [_moveTouches removeAllObjects]; + [_onInterceptTouchEventView removeAllObjects]; + [_onInterceptPullUpEventView removeAllObjects]; +} + +- (void)touchesCancelled:(NSSet *)touches withEvent:(UIEvent *)event { + [super touchesCancelled:touches withEvent:event]; + if ([_bridge.customTouchHandler respondsToSelector:@selector(customTouchesCancelled:withEvent:)]) { + BOOL shouldRecursive = [_bridge.customTouchHandler customTouchesCancelled:touches withEvent:event]; + if (!shouldRecursive) { + return; + } + } + + [_moveViews removeAllObjects]; + [_moveTouches removeAllObjects]; + + UITouch *touch = [touches anyObject]; + { + UIView *touchView = [touch view]?:_touchBeganView; + CGPoint locationPoint = [touch locationInView:touchView]; + touchView = touchView?:[self.view.window hitTest:locationPoint withEvent:event]; + NSDictionary *result = [self responseViewForAction:@[@"onTouchCancel", @"onPressOut", @"onClick"] inView:touchView + atPoint:locationPoint]; + UIView *clickView = result[@"onClick"][@"view"]; + UIView *view = result[@"onTouchCancel"][@"view"]; + if (view) { + NSInteger index = [result[@"onTouchCancel"][@"index"] integerValue]; + NSInteger clickIndex = NSNotFound; + if (clickView) { + clickIndex = [result[@"onClick"][@"index"] integerValue]; + } + + if (clickView == nil || (index <= clickIndex && clickIndex != NSNotFound)) { + CGPoint point = [touch locationInView:view]; + point = [view convertPoint:point toView:_rootView]; + if (view.onTouchCancel) { + if ([self checkViewBelongToTouchHandler:view]) { +// view.onTouchCancel(@{ @"page_x": @(point.x), @"page_y": @(point.y) }); + const char *name = hippy::kTouchCancelEvent; + view.onTouchCancel(point, + [self canCapture:name], + [self canBubble:name], + [self canBePreventedByInCapturing:name], + [self canBePreventInBubbling:name]); + } + } + } + } + + if (result[@"onPressOut"][@"view"]) { + UIView *pressOutView = result[@"onPressOut"][@"view"]; + if (pressOutView == _onPressInView && pressOutView.onPressOut) { + if ([self checkViewBelongToTouchHandler:pressOutView]) { + const char *name = hippy::kPressOut; + pressOutView.onPressOut(CGPointZero, + [self canCapture:name], + [self canBubble:name], + [self canBePreventedByInCapturing:name], + [self canBePreventInBubbling:name]); + } + } + } + } + self.state = UIGestureRecognizerStateCancelled; + self.enabled = NO; + self.enabled = YES; + [_onInterceptTouchEventView removeAllObjects]; + [_onInterceptPullUpEventView removeAllObjects]; +} + +- (void)touchesMoved:(NSSet *)touches withEvent:(UIEvent *)event { + [super touchesMoved:touches withEvent:event]; + if ([_bridge.customTouchHandler respondsToSelector:@selector(customTouchesMoved:withEvent:)]) { + BOOL shouldRecursive = [_bridge.customTouchHandler customTouchesMoved:touches withEvent:event]; + if (!shouldRecursive) { + return; + } + } + + UITouch *touch = [touches anyObject]; + CGPoint point = [touch locationInView:touch.view]; + + float dis = hypotf(_startPoint.x - point.x, _startPoint.y - point.y); + if (dis < 1.f) { + return; + } + [self clearTimer]; + _onClickView = nil; + + { + NSInteger index = [_moveTouches indexOfObject:touch]; + NSDictionary *result = nil; + if (index != NSNotFound) { + result = _moveViews[index]; + } else { + UIView *touchView = [touch view]?:_touchBeganView; + CGPoint locationPoint = [touch locationInView:touchView]; + touchView = touchView?:[self.view.window hitTest:locationPoint withEvent:event]; + NSDictionary *result = [self responseViewForAction:@[@"onTouchMove", @"onPressOut", @"onClick"] inView:touchView + atPoint:locationPoint]; + [_moveTouches addObject:touch]; + [_moveViews addObject:result]; + } + + if (_bPressIn) { + if (result[@"onLongClick"][@"view"]) { + _bLongClick = NO; + [self clearLongClickTimer]; + } + } + + UIView *clickView = result[@"onClick"][@"view"]; + UIView *view = result[@"onTouchMove"][@"view"]; + if (view) { + NSInteger index = [result[@"onTouchMove"][@"index"] integerValue]; + NSInteger clickIndex = NSNotFound; + if (clickView) { + clickIndex = [result[@"onClick"][@"index"] integerValue]; + } + + if (clickView == nil || (index <= clickIndex && clickIndex != NSNotFound)) { + if (view.onTouchMove) { + CGPoint point = [touch locationInView:view]; + point = [view convertPoint:point toView:_rootView]; + if ([self checkViewBelongToTouchHandler:view]) { +// view.onTouchMove(@{ @"page_x": @(point.x), @"page_y": @(point.y) }); + const char *name = hippy::kTouchMoveEvent; + view.onTouchMove(point, + [self canCapture:name], + [self canBubble:name], + [self canBePreventedByInCapturing:name], + [self canBePreventInBubbling:name]); + } + } + } + } + } + self.state = UIGestureRecognizerStateChanged; +} + +- (BOOL)checkViewBelongToTouchHandler:(UIView *)view { + NSNumber *reactTag = [view hippyTag]; + UIView *checkView = [_bridge.uiManager viewForHippyTag:reactTag onRootTag:view.rootTag]; + if (!checkView) { + NSNumber *viewRootTag = [view rootTag]; + NSNumber *rootViewTag = [_rootView hippyTag]; + if (rootViewTag) { + return [viewRootTag isEqualToNumber:rootViewTag]; + } + } + return checkView == view; +} + +- (void)clearTimer { + if (_toucheBeginTimer) { + [_toucheBeginTimer invalidate]; + _toucheBeginTimer = nil; + } +} + +- (void)clearLongClickTimer { + if (_touchLongTimer) { + [_touchLongTimer invalidate]; + _touchLongTimer = nil; + } +} + +- (void)scheduleTimer:(__unused NSTimer *)timer { + if (!_bPressIn) { + if (_onPressInView && _onPressInView.onPressIn) { + if ([self checkViewBelongToTouchHandler:_onPressInView]) { + const char *name = hippy::kPressIn; + _onPressInView.onPressIn(CGPointZero, + [self canCapture:name], + [self canBubble:name], + [self canBePreventedByInCapturing:name], + [self canBePreventInBubbling:name]); + } + } + _bPressIn = YES; + } + + // self.state = UIGestureRecognizerStateEnded; +} + +- (void)longClickTimer:(__unused NSTimer *)timer { + if (!_bLongClick) { + _bLongClick = YES; + if (_onLongClickView && _onLongClickView.onLongClick) { + if ([self checkViewBelongToTouchHandler:_onLongClickView]) { +// _onLongClickView.onLongClick(@{}); + const char *name = hippy::kLongClickEvent; + _onLongClickView.onLongClick(CGPointZero, + [self canCapture:name], + [self canBubble:name], + [self canBePreventedByInCapturing:name], + [self canBePreventInBubbling:name]); + } + } + } +} + +- (UIView *)rootView:(UIView *)view { + while (view.superview.hippyTag) { + view = view.superview; + } + return view; +} + +- (NSDictionary *)responseViewForAction:(NSArray *)actions inView:(UIView *)targetView atPoint:(CGPoint)point { + NSDictionary *result = [self nextResponseViewForAction:actions inView:targetView atPoint:point]; + NSNumber *innerTag = [targetView hippyTagAtPoint:point]; + if (innerTag && ![targetView.hippyTag isEqual:innerTag]) { + UIView *innerView = [_bridge.uiManager viewForHippyTag:innerTag onRootTag:targetView.rootTag]; + NSDictionary *innerResult = [self nextResponseViewForAction:actions inView:innerView atPoint:point]; + NSMutableDictionary *mergedResult = [result mutableCopy]; + [mergedResult addEntriesFromDictionary:innerResult]; + return mergedResult; + } + return result; +} + +- (NSDictionary *)nextResponseViewForAction:(NSArray *)actions inView:(UIView *)targetView atPoint:(CGPoint)point { + NSMutableDictionary *result = [NSMutableDictionary new]; + NSMutableArray *findActions = [NSMutableArray arrayWithArray:actions]; + UIView *view = (UIView *)targetView; + NSInteger index = 0; + while (view) { + BOOL onInterceptTouchEvent = view.onInterceptTouchEvent; + BOOL onInterceptPullUpEvent = view.onInterceptPullUpEvent; + if (onInterceptTouchEvent) { + findActions = [NSMutableArray arrayWithArray:actions]; + [result removeAllObjects]; + [_onInterceptTouchEventView addObject:view]; + } + + if (onInterceptPullUpEvent) { + if (point.y < _startPoint.y) { + findActions = [NSMutableArray arrayWithArray:actions]; + [result removeAllObjects]; + [_onInterceptPullUpEventView addObject:view]; + } + } + BOOL touchInterceptEvent = onInterceptTouchEvent || onInterceptPullUpEvent; + + if ((touchInterceptEvent && findActions.count == 0) || [view isKindOfClass:NSClassFromString(@"HippyRootContentView")]) { + break; + } else { + if ([findActions containsObject:@"onPressIn"] && view.onPressIn) { + if (!result[@"onClick"]) { + [result setValue:@{ @"view": view, @"index": @(index) } forKey:@"onPressIn"]; + } + [findActions removeObject:@"onPressIn"]; + } + + if ([findActions containsObject:@"onPressOut"] && view.onPressOut) { + [result setValue:@{ @"view": view, @"index": @(index) } forKey:@"onPressOut"]; + [findActions removeObject:@"onPressOut"]; + } + + if ([findActions containsObject:@"onClick"] && view.onClick) { + [result setValue:@{ @"view": view, @"index": @(index) } forKey:@"onClick"]; + [findActions removeObject:@"onClick"]; + } + + if ([findActions containsObject:@"onLongClick"] && view.onLongClick) { + [result setValue:@{ @"view": view, @"index": @(index) } forKey:@"onLongClick"]; + [findActions removeObject:@"onLongClick"]; + } + + if ([findActions containsObject:@"onTouchDown"] && view.onTouchDown) { + [result setValue:@{ @"view": view, @"index": @(index) } forKey:@"onTouchDown"]; + [findActions removeObject:@"onTouchDown"]; + } + + if ([findActions containsObject:@"onTouchMove"] && view.onTouchMove) { + [result setValue:@{ @"view": view, @"index": @(index) } forKey:@"onTouchMove"]; + [findActions removeObject:@"onTouchMove"]; + } + if ([findActions containsObject:@"onTouchCancel"] && view.onTouchCancel) { + [result setValue:@{ @"view": view, @"index": @(index) } forKey:@"onTouchCancel"]; + [findActions removeObject:@"onTouchCancel"]; + } + + if ([findActions containsObject:@"onTouchEnd"] && view.onTouchEnd) { + [result setValue:@{ @"view": view, @"index": @(index) } forKey:@"onTouchEnd"]; + [findActions removeObject:@"onTouchEnd"]; + } + + if (touchInterceptEvent) + break; + view = [view nextResponseViewAtPoint:point]; + index++; + } + } + return result; +} + +- (BOOL)gestureRecognizer:(__unused UIGestureRecognizer *)gestureRecognizer shouldReceiveTouch:(UITouch *)touch { + UIView *touchView = [touch view]; + while (touchView && ![touchView hippyTag]) { + NSArray *touchGestureRegs = [touchView gestureRecognizers]; + for (UIGestureRecognizer *touchGes in touchGestureRegs) { + if (![self canPreventGestureRecognizer:touchGes]) { + return NO; + } + } + touchView = [touchView superview]; + } + if ([self isYYTextView:touch.view]) { + return NO; + } + + if ([touch.view isKindOfClass:[UIButton class]]) { + return NO; + } + + __block BOOL ret = YES; + + [touch.view hippyLoopSuperViewHierarchy:^(UIView *view, BOOL *stop) { + if ([view conformsToProtocol:@protocol(HippyScrollProtocol)]) { + if ([(id)view isManualScrolling]) { + ret = NO; + *stop = YES; + } + } + }]; + + return ret; +} + +- (BOOL)isYYTextView:(UIView *)view { + Class yyTextViewClass = NSClassFromString(@"YYTextView"); + Class yyTextSelectionView = NSClassFromString(@"YYTextSelectionView"); + Class yyTextContainerView = NSClassFromString(@"YYTextContainerView"); + + if ([view isKindOfClass:yyTextViewClass] || [view isKindOfClass:yyTextSelectionView] || [view isKindOfClass:yyTextContainerView]) { + return YES; + } + + return NO; +} + +- (void)cancelTouch { + if (_onPressInView) { + _bPressIn = NO; + if (_onPressInView.onPressOut) { + if ([self checkViewBelongToTouchHandler:_onPressInView]) { + const char *name = hippy::kPressOut; + _onPressInView.onPressOut(CGPointZero, + [self canCapture:name], + [self canBubble:name], + [self canBePreventedByInCapturing:name], + [self canBePreventInBubbling:name]); + } + } + } + _bLongClick = NO; + [self clearTimer]; + [self clearLongClickTimer]; + self.enabled = NO; + self.enabled = YES; +} + +- (void)reset { + if ([_bridge.customTouchHandler respondsToSelector:@selector(customReset)]) { + BOOL shouldRecursive = [_bridge.customTouchHandler customReset]; + if (!shouldRecursive) { + return; + } + } + + if (_onPressInView) { + _bPressIn = NO; + if (_onPressInView.onPressOut) { + if ([self checkViewBelongToTouchHandler:_onPressInView]) { + const char *name = hippy::kPressOut; + _onPressInView.onPressOut(CGPointZero, + [self canCapture:name], + [self canBubble:name], + [self canBePreventedByInCapturing:name], + [self canBePreventInBubbling:name]); + } + } + } + [self clearTimer]; + _bLongClick = NO; + [self clearLongClickTimer]; + [super reset]; +} + +- (BOOL)canPreventGestureRecognizer:(__unused UIGestureRecognizer *)preventedGestureRecognizer { + UIView *gestureView = [preventedGestureRecognizer view]; + for (UIView *view in _onInterceptTouchEventView) { + if ([gestureView isDescendantOfView:view] && gestureView != view && ![gestureView hippyTag]) { + return YES; + } + } + for (UIView *view in _onInterceptPullUpEventView) { + if ([gestureView isDescendantOfView:view] && gestureView != view && ![gestureView hippyTag]) { + return YES; + } + } + if ([preventedGestureRecognizer isKindOfClass:[self class]]) { + UIView *currentHandlerView = [self view]; + BOOL canPreventGestureRecognizer = [currentHandlerView isDescendantOfView:gestureView]; + return canPreventGestureRecognizer; + } + return NO; +} + +- (BOOL)canBePreventedByGestureRecognizer:(UIGestureRecognizer *)preventingGestureRecognizer { + // We fail in favour of other external gesture recognizers. + // iOS will ask `delegate`'s opinion about this gesture recognizer little bit later. + if (![preventingGestureRecognizer.view isDescendantOfView:_rootView]) { + return NO; + } + else if ([preventingGestureRecognizer isKindOfClass:[self class]]) { + UIView *currentHandlerView = [self view]; + UIView *gestureView = [preventingGestureRecognizer view]; + BOOL canPreventGestureRecognizer = [currentHandlerView isDescendantOfView:gestureView]; + return !canPreventGestureRecognizer; + } + else { + return ![preventingGestureRecognizer.view isDescendantOfView:self.view]; + } +} + +- (BOOL)gestureRecognizer:(__unused UIGestureRecognizer *)gestureRecognizer + shouldRequireFailureOfGestureRecognizer:(UIGestureRecognizer *)otherGestureRecognizer { + // Same condition for `failure of` as for `be prevented by`. + + return [self canBePreventedByGestureRecognizer:otherGestureRecognizer]; +} + + +#pragma mark - Event Helper Methods + +static BOOL IsGestureEvent(const char *name) { + if (!name) { + return NO; + } + if (0 == strcmp(name, hippy::kClickEvent) || + 0 == strcmp(name, hippy::kLongClickEvent) || + 0 == strcmp(name, hippy::kPressIn) || + 0 == strcmp(name, hippy::kPressOut) || + 0 == strcmp(name, hippy::kTouchStartEvent) || + 0 == strcmp(name, hippy::kTouchEndEvent) || + 0 == strcmp(name, hippy::kTouchMoveEvent) || + 0 == strcmp(name, hippy::kTouchCancelEvent)) { + return YES; + } + return NO; +} + +- (BOOL)canCapture:(const char *)name { + if (!name) { + return YES; + } + return IsGestureEvent(name); +} + +- (BOOL)canBubble:(const char *)name { + if (!name) { + return YES; + } + return IsGestureEvent(name); +} + +- (BOOL)canBePreventedByInCapturing:(const char *)name { + return NO; +} + +- (BOOL)canBePreventInBubbling:(const char *)name { + return NO; +} + + +@end + diff --git a/renderer/native/ios/utils/NativeRenderGradientObject.m b/renderer/native/ios/utils/NativeRenderGradientObject.m index 6bd8dd357dc..eb7812b68dd 100644 --- a/renderer/native/ios/utils/NativeRenderGradientObject.m +++ b/renderer/native/ios/utils/NativeRenderGradientObject.m @@ -20,7 +20,7 @@ * limitations under the License. */ -#import "HippyAsserts.h" +#import "HippyAssert.h" #import "HippyUtils.h" #import "HippyBorderDrawing.h" #import "NativeRenderGradientObject.h" diff --git a/renderer/voltron/CHANGELOG.md b/renderer/voltron/CHANGELOG.md index 1730c7b4d13..d8ad34d21c6 100644 --- a/renderer/voltron/CHANGELOG.md +++ b/renderer/voltron/CHANGELOG.md @@ -1,3 +1,15 @@ +## 0.0.27 + +- resolve merge conflicts + +## 0.0.26 + +- update ffi_manager to 0.0.6 + +## 0.0.25 + +- add voltron_screen_info plugin + ## 0.0.24 - fix width calculate error and add modal navigator param diff --git a/renderer/voltron/lib/bridge/render_bridge.dart b/renderer/voltron/lib/bridge/render_bridge.dart index 58c575b1467..2ae1f629263 100644 --- a/renderer/voltron/lib/bridge/render_bridge.dart +++ b/renderer/voltron/lib/bridge/render_bridge.dart @@ -72,7 +72,7 @@ class VoltronRenderBridgeManager implements Destroyable { } int createNativeRenderManager() { - return VoltronRenderApi.createNativeRender(ScreenUtil.getInstance().screenDensity); + return VoltronRenderApi.createNativeRender(ScreenUtil.getInstance().scale); } Future destroyNativeRenderManager() async { diff --git a/renderer/voltron/lib/controller/controller.dart b/renderer/voltron/lib/controller/controller.dart index ec63e5f1753..06a43039048 100644 --- a/renderer/voltron/lib/controller/controller.dart +++ b/renderer/voltron/lib/controller/controller.dart @@ -502,7 +502,7 @@ abstract class VoltronViewController 0) { @@ -515,7 +515,7 @@ abstract class VoltronViewController(); static final ScreenUtil _singleton = ScreenUtil(); static ScreenUtil getInstance() { - _singleton._init(); return _singleton; } - Future ensurePhysicalSizeReady () { + Future initScreen({ScreenInfoSource screenInfoSource = ScreenInfoSource.flutter}) async { + if (screenInfoSource == ScreenInfoSource.flutter) { + // use ui.window, better used in flutter app + await _initFromUIWindow(); + } else if (screenInfoSource == ScreenInfoSource.native) { + // use method channel, faster when use cached flutter engine in native app + await _initFromMethodChannel(); + } + } + + Future ensurePhysicalSizeReady() { if (ui.window.physicalSize.width > 0 && ui.window.physicalSize.height > 0) { return Future.value(); } - var ob = ScreenObserver( - onScreenReady: () { - ensureSizeCompleter.complete(); - } - ); + var ob = ScreenObserver(onScreenReady: () { + ensureSizeCompleter.complete(); + }); WidgetsBinding.instance.addObserver(ob); return ensureSizeCompleter.future.then((value) => WidgetsBinding.instance.removeObserver(ob)); } - void _init() { + Future _initFromUIWindow() async { + await ensurePhysicalSizeReady(); var mediaQuery = MediaQueryData.fromWindow(ui.window); - _physicalSize = ui.window.physicalSize; - if (_mediaQueryData != mediaQuery) { - _mediaQueryData = mediaQuery; - _screenWidth = mediaQuery.size.width; - _screenHeight = mediaQuery.size.height; - _screenDensity = mediaQuery.devicePixelRatio; - _statusBarHeight = mediaQuery.padding.top; - _bottomBarHeight = mediaQuery.padding.bottom; - _fontScale = mediaQuery.textScaleFactor; - _appBarHeight = kToolbarHeight; - _brightness = mediaQuery.platformBrightness; + _screenWidth = mediaQuery.size.width; + _screenHeight = mediaQuery.size.height; + _windowWidth = _screenWidth; + _windowHeight = _windowHeight; + _scale = mediaQuery.devicePixelRatio; + _fontScale = mediaQuery.textScaleFactor; + _statusBarHeight = mediaQuery.padding.top; + // 这里跟Hippy Native保持一致,只取安卓值即可 + if (Platform.isAndroid) { + _navigationBarHeight = mediaQuery.padding.bottom; + } + _brightness = mediaQuery.platformBrightness; + } + + Future _initFromMethodChannel() async { + var voltronScreenInfoPlugin = VoltronScreenInfoPlugin(); + if (Platform.isAndroid) { + var androidScreenInfo = await voltronScreenInfoPlugin.androidInfo; + _screenWidth = androidScreenInfo.screenWidth; + _screenHeight = androidScreenInfo.screenHeight; + _windowWidth = androidScreenInfo.screenWidth; + _windowHeight = androidScreenInfo.screenHeight; + _scale = androidScreenInfo.scale; + _fontScale = androidScreenInfo.fontScale; + _densityDpi = androidScreenInfo.densityDpi; + _statusBarHeight = androidScreenInfo.statusBarHeight; + _navigationBarHeight = androidScreenInfo.navigationBarHeight; + _brightness = androidScreenInfo.nightMode ? Brightness.dark : Brightness.light; + } else if (Platform.isIOS) { + var iosScreenInfo = await voltronScreenInfoPlugin.iosInfo; + _screenWidth = iosScreenInfo.screenWidth; + _screenHeight = iosScreenInfo.screenHeight; + _windowWidth = iosScreenInfo.screenWidth; + _windowHeight = iosScreenInfo.screenHeight; + _scale = iosScreenInfo.scale; + _fontScale = iosScreenInfo.fontScale; + _statusBarHeight = iosScreenInfo.statusBarHeight; + _brightness = iosScreenInfo.nightMode ? Brightness.dark : Brightness.light; } } @@ -80,27 +123,29 @@ class ScreenUtil { /// 屏幕 高 dp double get screenHeight => _screenHeight; - /// appBar height - /// appBar 高 dp - double get appBarHeight => _appBarHeight; + /// screen width + /// 屏幕 宽 dp + double get windowWidth => _windowWidth; + + /// screen width + /// 屏幕 宽 dp + double get windowHeight => _windowHeight; /// screen density /// 屏幕 像素密度 - double get screenDensity => _screenDensity; + double get scale => _scale; + + double get fontScale => _fontScale; + + /// only in android + int get densityDpi => _densityDpi; /// status bar Height /// 状态栏高度 dp double get statusBarHeight => _statusBarHeight; /// bottom bar Height dp - double get bottomBarHeight => _bottomBarHeight; - - /// media Query Data - MediaQueryData? get mediaQueryData => _mediaQueryData; - - double get fontScale => _fontScale; - - Size? get physicalSize => _physicalSize; + double get navigationBarHeight => _navigationBarHeight; Brightness get brightness => _brightness; @@ -166,7 +211,7 @@ class ScreenObserver extends WidgetsBindingObserver { ScreenObserver({required this.onScreenReady}); - void didChangeMetrics () { + void didChangeMetrics() { if (ui.window.physicalSize.width > 0 && ui.window.physicalSize.height > 0 && !isReady) { isReady = true; onScreenReady(); diff --git a/renderer/voltron/pubspec.yaml b/renderer/voltron/pubspec.yaml index e423c0a45f9..11861d690e2 100644 --- a/renderer/voltron/pubspec.yaml +++ b/renderer/voltron/pubspec.yaml @@ -23,7 +23,7 @@ name: voltron_renderer description: voltron renderer -version: 0.0.24 +version: 0.0.27 homepage: https://hippyjs.org repository: https://github.com/Tencent/Hippy @@ -46,7 +46,8 @@ dependencies: waterfall_flow: ^3.0.2 charset: ^1.1.0 ffi: ^2.0.0 - voltron_ffi: 0.0.5 + voltron_ffi: 0.0.6 + voltron_screen_info: 0.0.1 dev_dependencies: flutter_test: