From e5fcf2f82c3cf2ce9ea78f38cee74bbcf8e323e2 Mon Sep 17 00:00:00 2001 From: wwwcg Date: Tue, 10 Sep 2024 18:26:51 +0800 Subject: [PATCH] refactor(ios): add a uiManager getter for UIView (#4022) For compatibility, some third-party components want to get UIManager or HippyBridge directly through UIView, although there is a slight performance penalty doing this. --- renderer/native/ios/renderer/HippyUIManager.h | 3 ++ .../native/ios/renderer/HippyUIManager.mm | 19 ++++++++-- ....mm => HippyNextBaseListItemViewManager.m} | 0 .../listview/HippyNextBaseListView.mm | 9 +++-- ...e.mm => HippyNextBaseListViewDataSource.m} | 0 ...ager.mm => HippyNextBaseListViewManager.m} | 2 +- .../NativeRenderSmartViewPagerView.mm | 8 ++-- .../renderer/component/view/UIView+Render.h | 37 ++++++++++++++++++ .../renderer/component/view/UIView+Render.m | 38 +++++++++++++++++++ .../waterfalllist/HippyWaterfallView.h | 9 ----- .../waterfalllist/HippyWaterfallView.mm | 8 ++-- tests/ios/HippyUIViewCategoryTest.m | 22 ++++++++++- 12 files changed, 127 insertions(+), 28 deletions(-) rename renderer/native/ios/renderer/component/listview/{HippyNextBaseListItemViewManager.mm => HippyNextBaseListItemViewManager.m} (100%) rename renderer/native/ios/renderer/component/listview/{HippyNextBaseListViewDataSource.mm => HippyNextBaseListViewDataSource.m} (100%) rename renderer/native/ios/renderer/component/listview/{HippyNextBaseListViewManager.mm => HippyNextBaseListViewManager.m} (98%) create mode 100644 renderer/native/ios/renderer/component/view/UIView+Render.h create mode 100644 renderer/native/ios/renderer/component/view/UIView+Render.m diff --git a/renderer/native/ios/renderer/HippyUIManager.h b/renderer/native/ios/renderer/HippyUIManager.h index d10ce61ca39..63d88dd132e 100644 --- a/renderer/native/ios/renderer/HippyUIManager.h +++ b/renderer/native/ios/renderer/HippyUIManager.h @@ -33,6 +33,7 @@ @class HippyComponentMap; @protocol HippyImageProviderProtocol; +NS_ASSUME_NONNULL_BEGIN /** * Posted whenever a new root view is registered with HippyUIManager. The userInfo property @@ -156,3 +157,5 @@ HIPPY_EXTERN NSString *const HippyUIManagerDidEndBatchNotification; @property (nonatomic, strong, readonly) id customTouchHandler; @end + +NS_ASSUME_NONNULL_END diff --git a/renderer/native/ios/renderer/HippyUIManager.mm b/renderer/native/ios/renderer/HippyUIManager.mm index 8643e9f0568..2419d3928db 100644 --- a/renderer/native/ios/renderer/HippyUIManager.mm +++ b/renderer/native/ios/renderer/HippyUIManager.mm @@ -162,6 +162,18 @@ static void NativeRenderTraverseViewNodes(id view, void (^block) } } + +@interface UIView (HippyUIManagerPrivate) + +/// Bind UIView with HippyUIManager +/// This is a convenient method for UIView to get HippyUIManager instance. +/// - Parameter uiManager: HippyUIManager instance +- (void)setUiManager:(HippyUIManager *)uiManager; + +@end + +#pragma mark - + #define AssertMainQueue() NSAssert(HippyIsMainQueue(), @"This function must be called on the main thread") NSString *const HippyUIManagerDidRegisterRootViewNotification = @"HippyUIManagerDidRegisterRootViewNotification"; @@ -318,8 +330,8 @@ - (void)registerRootView:(UIView *)rootView asRootNode:(std::weak_ptr) // Register view [_viewRegistry addRootComponent:rootView rootNode:rootNode forTag:hippyTag]; - - [rootView addObserver:self forKeyPath:@"frame" + rootView.uiManager = self; + [rootView addObserver:self forKeyPath:@"frame" options:(NSKeyValueObservingOptionOld | NSKeyValueObservingOptionNew) context:NULL]; CGRect frame = rootView.frame; @@ -516,6 +528,7 @@ - (UIView *)createViewFromShadowView:(HippyShadowView *)shadowView { view.viewName = viewName; view.rootTag = rootTag; view.hippyShadowView = shadowView; + view.uiManager = self; [componentData setProps:props forView:view]; // Must be done before bgColor to prevent wrong default } } @@ -1523,7 +1536,5 @@ - (void)setCustomTouchHandler:(id)customTouchHa objc_setAssociatedObject(self, @selector(customTouchHandler), customTouchHandler, OBJC_ASSOCIATION_RETAIN); } - @end - diff --git a/renderer/native/ios/renderer/component/listview/HippyNextBaseListItemViewManager.mm b/renderer/native/ios/renderer/component/listview/HippyNextBaseListItemViewManager.m similarity index 100% rename from renderer/native/ios/renderer/component/listview/HippyNextBaseListItemViewManager.mm rename to renderer/native/ios/renderer/component/listview/HippyNextBaseListItemViewManager.m diff --git a/renderer/native/ios/renderer/component/listview/HippyNextBaseListView.mm b/renderer/native/ios/renderer/component/listview/HippyNextBaseListView.mm index 21c2c78b867..40daabd1b53 100644 --- a/renderer/native/ios/renderer/component/listview/HippyNextBaseListView.mm +++ b/renderer/native/ios/renderer/component/listview/HippyNextBaseListView.mm @@ -31,6 +31,7 @@ #import "HippyShadowView.h" #import "UIView+DirectionalLayout.h" #import "UIView+Hippy.h" +#import "UIView+Render.h" #import "HippyShadowListView.h" static NSString *const kCellIdentifier = @"HippyListCellIdentifier"; @@ -48,8 +49,8 @@ @implementation HippyNextBaseListView #pragma mark - Life Cycle -- (instancetype)initWithBridge:(HippyBridge *)bridge { - if (self = [super initWithBridge:bridge]) { +- (instancetype)initWithFrame:(CGRect)frame { + if (self = [super initWithFrame:frame]) { _isInitialListReady = NO; self.preloadItemNumber = 1; } @@ -220,7 +221,7 @@ - (UICollectionReusableView *)collectionView:(UICollectionView *)collectionView forIndexPath:indexPath]; HippyShadowView *headerRenderObject = [self.dataSource headerForSection:section]; if (headerRenderObject && [headerRenderObject isKindOfClass:[HippyShadowView class]]) { - UIView *headerView = [self.bridge.uiManager createViewForShadowListItem:headerRenderObject]; + UIView *headerView = [self.uiManager createViewForShadowListItem:headerRenderObject]; CGRect frame = headerView.frame; frame.origin = CGPointZero; headerView.frame = frame; @@ -272,7 +273,7 @@ - (UICollectionViewCell *)collectionView:(UICollectionView *)collectionView cell cellView = cachedVisibleCellView; HippyLogTrace(@"🟢 use cached visible cellView at %@ for %@", indexPath, shadowView.hippyTag); } else { - cellView = [self.bridge.uiManager createViewForShadowListItem:shadowView]; + cellView = [self.uiManager createViewForShadowListItem:shadowView]; [_cachedWeakCellViews setObject:cellView forKey:shadowView.hippyTag]; HippyLogTrace(@"🟡 create cellView at %@ for %@", indexPath, shadowView.hippyTag); } diff --git a/renderer/native/ios/renderer/component/listview/HippyNextBaseListViewDataSource.mm b/renderer/native/ios/renderer/component/listview/HippyNextBaseListViewDataSource.m similarity index 100% rename from renderer/native/ios/renderer/component/listview/HippyNextBaseListViewDataSource.mm rename to renderer/native/ios/renderer/component/listview/HippyNextBaseListViewDataSource.m diff --git a/renderer/native/ios/renderer/component/listview/HippyNextBaseListViewManager.mm b/renderer/native/ios/renderer/component/listview/HippyNextBaseListViewManager.m similarity index 98% rename from renderer/native/ios/renderer/component/listview/HippyNextBaseListViewManager.mm rename to renderer/native/ios/renderer/component/listview/HippyNextBaseListViewManager.m index 71ad81c91b2..48d57088841 100644 --- a/renderer/native/ios/renderer/component/listview/HippyNextBaseListViewManager.mm +++ b/renderer/native/ios/renderer/component/listview/HippyNextBaseListViewManager.m @@ -48,7 +48,7 @@ @implementation HippyNextBaseListViewManager HIPPY_EXPORT_VIEW_PROPERTY(horizontal, BOOL) - (UIView *)view { - return [[HippyNextBaseListView alloc] initWithBridge:self.bridge]; + return [[HippyNextBaseListView alloc] init]; } - (HippyShadowView *)shadowView { diff --git a/renderer/native/ios/renderer/component/smartViewPager/NativeRenderSmartViewPagerView.mm b/renderer/native/ios/renderer/component/smartViewPager/NativeRenderSmartViewPagerView.mm index 6c9fa2613d4..b8c779dc11d 100644 --- a/renderer/native/ios/renderer/component/smartViewPager/NativeRenderSmartViewPagerView.mm +++ b/renderer/native/ios/renderer/component/smartViewPager/NativeRenderSmartViewPagerView.mm @@ -30,7 +30,7 @@ #import "HippyScrollProtocol.h" #import "UIView+MountEvent.h" #import "UIView+Hippy.h" - +#import "UIView+Render.h" #include static NSInteger kInfiniteLoopBegin = 2; @@ -58,8 +58,8 @@ - (void)setPreviousMargin:(CGFloat)previousMargin nextMargin:(CGFloat)nextMargin @implementation NativeRenderSmartViewPagerView #pragma mark Life Cycle -- (instancetype)initWithBridge:(HippyBridge *)bridge { - if (self = [super initWithBridge:bridge]) { +- (instancetype)initWithFrame:(CGRect)frame { + if (self = [super initWithFrame:frame]) { _isInitialListReady = NO; _dataSource = [[HippyNextBaseListViewDataSource alloc] initWithDataSource:nil itemViewName:[self compoentItemName] @@ -356,7 +356,7 @@ - (void)collectionView:(UICollectionView *)collectionView NSIndexPath *adjustIndexPath = [NSIndexPath indexPathForRow:cellIndex inSection:indexPath.section]; HippyWaterfallViewCell *hpCell = (HippyWaterfallViewCell *)cell; HippyShadowView *renderObject = [_dataSource cellForIndexPath:adjustIndexPath]; - UIView *cellView = [self.bridge.uiManager createViewForShadowListItem:renderObject]; + UIView *cellView = [self.uiManager createViewForShadowListItem:renderObject]; hpCell.cellView = cellView; cellView.parent = self; } diff --git a/renderer/native/ios/renderer/component/view/UIView+Render.h b/renderer/native/ios/renderer/component/view/UIView+Render.h new file mode 100644 index 00000000000..d7d508ba879 --- /dev/null +++ b/renderer/native/ios/renderer/component/view/UIView+Render.h @@ -0,0 +1,37 @@ +/*! + * 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 + +NS_ASSUME_NONNULL_BEGIN + +@class HippyUIManager; +/// UIView's UIManager category +@interface UIView (HippyUIManager) + +/// Convenient method to get HippyUIManager instance, +/// UIView's uiManager property is set when created. +- (nullable HippyUIManager *)uiManager; + +@end + +NS_ASSUME_NONNULL_END diff --git a/renderer/native/ios/renderer/component/view/UIView+Render.m b/renderer/native/ios/renderer/component/view/UIView+Render.m new file mode 100644 index 00000000000..d751e6cd7f6 --- /dev/null +++ b/renderer/native/ios/renderer/component/view/UIView+Render.m @@ -0,0 +1,38 @@ +/*! + * 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 "UIView+Render.h" +#import + +@implementation UIView (HippyUIManager) + +- (HippyUIManager *)uiManager { + NSValue *weakValue = objc_getAssociatedObject(self, @selector(uiManager)); + return [weakValue nonretainedObjectValue]; +} + +- (void)setUiManager:(HippyUIManager *)uiManager { + NSValue *weakValue = [NSValue valueWithNonretainedObject:uiManager]; + objc_setAssociatedObject(self, @selector(uiManager), weakValue, OBJC_ASSOCIATION_RETAIN_NONATOMIC); +} + +@end diff --git a/renderer/native/ios/renderer/component/waterfalllist/HippyWaterfallView.h b/renderer/native/ios/renderer/component/waterfalllist/HippyWaterfallView.h index c25df712d16..35025b65395 100644 --- a/renderer/native/ios/renderer/component/waterfalllist/HippyWaterfallView.h +++ b/renderer/native/ios/renderer/component/waterfalllist/HippyWaterfallView.h @@ -56,9 +56,6 @@ typedef NS_ENUM(NSInteger, NativeRenderScrollState) { BOOL _allowNextScrollNoMatterWhat; } -/// Weak ref of HippyBridge -@property (nonatomic, weak, readonly) HippyBridge *bridge; - /** * Content inset for HippyWaterfallView */ @@ -119,12 +116,6 @@ typedef NS_ENUM(NSInteger, NativeRenderScrollState) { @property (nonatomic, copy) HippyDirectEventBlock onRefresh; @property (nonatomic, copy) HippyDirectEventBlock onExposureReport; -/// Init method -/// - Parameter bridge: HippyBridge instance -- (instancetype)initWithBridge:(HippyBridge *)bridge NS_DESIGNATED_INITIALIZER; -- (instancetype)initWithFrame:(CGRect)frame NS_UNAVAILABLE; -- (instancetype)initWithCoder:(NSCoder *)coder NS_UNAVAILABLE; - /** * Initial collection view */ diff --git a/renderer/native/ios/renderer/component/waterfalllist/HippyWaterfallView.mm b/renderer/native/ios/renderer/component/waterfalllist/HippyWaterfallView.mm index 41e6e53a94e..7431c218e50 100644 --- a/renderer/native/ios/renderer/component/waterfalllist/HippyWaterfallView.mm +++ b/renderer/native/ios/renderer/component/waterfalllist/HippyWaterfallView.mm @@ -25,6 +25,7 @@ #import "HippyFooterRefresh.h" #import "HippyWaterfallItemView.h" #import "UIView+Hippy.h" +#import "UIView+Render.h" #import "HippyRefresh.h" #import "HippyWaterfallViewDataSource.h" #import "HippyShadowView.h" @@ -61,9 +62,8 @@ @implementation HippyWaterfallView { @synthesize contentSize; -- (instancetype)initWithBridge:(id)bridge { - if (self = [super initWithFrame:CGRectZero]) { - _bridge = bridge; +- (instancetype)initWithFrame:(CGRect)frame { + if (self = [super initWithFrame:frame]) { self.backgroundColor = [UIColor clearColor]; _scrollListeners = [NSHashTable weakObjectsHashTable]; _scrollEventThrottle = 100.f; @@ -266,7 +266,7 @@ - (UICollectionViewCell *)collectionView:(UICollectionView *)collectionView cell if (cachedCellView) { cellView = cachedCellView; } else { - cellView = [self.bridge.uiManager createViewForShadowListItem:shadowView]; + cellView = [self.uiManager createViewForShadowListItem:shadowView]; [_cachedWeakCellViews setObject:cellView forKey:shadowView.hippyTag]; } diff --git a/tests/ios/HippyUIViewCategoryTest.m b/tests/ios/HippyUIViewCategoryTest.m index 22705653f1c..d6fb521c9b7 100644 --- a/tests/ios/HippyUIViewCategoryTest.m +++ b/tests/ios/HippyUIViewCategoryTest.m @@ -21,7 +21,18 @@ */ #import -#import "UIView+Hippy.h" +#import +#import +#import + +@interface UIView (HippyUIManagerUnitTest) + +/// Bind UIView with HippyUIManager +/// This is a convenient method for UIView to get HippyUIManager instance. +/// - Parameter uiManager: HippyUIManager instance +- (void)setUiManager:(HippyUIManager *)uiManager; + +@end @interface HippyUIViewCategoryTest : XCTestCase @@ -47,8 +58,15 @@ - (void)testGetHippyRootView { XCTAssert([testView hippyRootView] == testSuperView); testView.hippyTag = @(11); XCTAssert([testView hippyRootView] == nil); +} + +- (void)testGetHippyUIManager { + UIView *testView = [UIView new]; + XCTAssertNil([testView uiManager]); - + HippyUIManager *uiManager = [[HippyUIManager alloc] init]; + XCTAssertNoThrow(testView.uiManager = uiManager); + XCTAssertTrue(testView.uiManager == uiManager); }