Skip to content
This repository was archived by the owner on Feb 25, 2025. It is now read-only.

Commit cfdcfca

Browse files
authored
[iOS] Fix platfotm view called multiple times (#19292)
1 parent ac769da commit cfdcfca

File tree

6 files changed

+95
-10
lines changed

6 files changed

+95
-10
lines changed

flow/embedded_views.h

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -124,7 +124,7 @@ class Mutator {
124124
//
125125
// For example consider the following stack: [T1, T2, T3], where T1 is the top
126126
// of the stack and T3 is the bottom of the stack. Applying this mutators stack
127-
// to a platform view P1 will result in T1(T2(T2(P1))).
127+
// to a platform view P1 will result in T1(T2(T3(P1))).
128128
class MutatorsStack {
129129
public:
130130
MutatorsStack() = default;

shell/platform/darwin/ios/framework/Source/FlutterPlatformViews.mm

Lines changed: 12 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -157,13 +157,13 @@
157157
NSObject<FlutterPlatformView>* embedded_view = [factory createWithFrame:CGRectZero
158158
viewIdentifier:viewId
159159
arguments:params];
160+
UIView* platform_view = [embedded_view view];
160161
// Set a unique view identifier, so the platform view can be identified in unit tests.
161-
[embedded_view view].accessibilityIdentifier =
162-
[NSString stringWithFormat:@"platform_view[%ld]", viewId];
162+
platform_view.accessibilityIdentifier = [NSString stringWithFormat:@"platform_view[%ld]", viewId];
163163
views_[viewId] = fml::scoped_nsobject<NSObject<FlutterPlatformView>>([embedded_view retain]);
164164

165165
FlutterTouchInterceptingView* touch_interceptor = [[[FlutterTouchInterceptingView alloc]
166-
initWithEmbeddedView:embedded_view.view
166+
initWithEmbeddedView:platform_view
167167
platformViewsController:GetWeakPtr()
168168
gestureRecognizersBlockingPolicy:gesture_recognizers_blocking_policies[viewType]]
169169
autorelease];
@@ -311,11 +311,11 @@
311311
views_to_recomposite_.insert(view_id);
312312
}
313313

314-
NSObject<FlutterPlatformView>* FlutterPlatformViewsController::GetPlatformViewByID(int view_id) {
314+
UIView* FlutterPlatformViewsController::GetPlatformViewByID(int view_id) {
315315
if (views_.empty()) {
316316
return nil;
317317
}
318-
return views_[view_id].get();
318+
return [touch_interceptors_[view_id].get() embeddedView];
319319
}
320320

321321
std::vector<SkCanvas*> FlutterPlatformViewsController::GetCurrentCanvases() {
@@ -451,7 +451,7 @@
451451
}
452452

453453
SkRect FlutterPlatformViewsController::GetPlatformViewRect(int view_id) {
454-
UIView* platform_view = [views_[view_id].get() view];
454+
UIView* platform_view = GetPlatformViewByID(view_id);
455455
UIScreen* screen = [UIScreen mainScreen];
456456
CGRect platform_view_cgrect = [platform_view convertRect:platform_view.bounds
457457
toView:flutter_view_];
@@ -747,6 +747,7 @@ - (instancetype)initWithTarget:(id)target
747747
@implementation FlutterTouchInterceptingView {
748748
fml::scoped_nsobject<DelayingGestureRecognizer> _delayingRecognizer;
749749
FlutterPlatformViewGestureRecognizersBlockingPolicy _blockingPolicy;
750+
UIView* _embeddedView;
750751
}
751752
- (instancetype)initWithEmbeddedView:(UIView*)embeddedView
752753
platformViewsController:
@@ -756,6 +757,7 @@ - (instancetype)initWithEmbeddedView:(UIView*)embeddedView
756757
self = [super initWithFrame:embeddedView.frame];
757758
if (self) {
758759
self.multipleTouchEnabled = YES;
760+
_embeddedView = embeddedView;
759761
embeddedView.autoresizingMask =
760762
(UIViewAutoresizingFlexibleWidth | UIViewAutoresizingFlexibleHeight);
761763

@@ -777,6 +779,10 @@ - (instancetype)initWithEmbeddedView:(UIView*)embeddedView
777779
return self;
778780
}
779781

782+
- (UIView*)embeddedView {
783+
return [[_embeddedView retain] autorelease];
784+
}
785+
780786
- (void)releaseGesture {
781787
_delayingRecognizer.get().state = UIGestureRecognizerStateFailed;
782788
}

shell/platform/darwin/ios/framework/Source/FlutterPlatformViewsTest.mm

Lines changed: 68 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -39,17 +39,31 @@ - (void)dealloc {
3939

4040
@interface FlutterPlatformViewsTestMockFlutterPlatformView : NSObject <FlutterPlatformView>
4141
@property(nonatomic, strong) UIView* view;
42+
@property(nonatomic, assign) BOOL viewCreated;
4243
@end
4344

4445
@implementation FlutterPlatformViewsTestMockFlutterPlatformView
4546

4647
- (instancetype)init {
4748
if (self = [super init]) {
4849
_view = [[FlutterPlatformViewsTestMockPlatformView alloc] init];
50+
_viewCreated = NO;
4951
}
5052
return self;
5153
}
5254

55+
- (UIView*)view {
56+
[self checkViewCreatedOnce];
57+
return _view;
58+
}
59+
60+
- (void)checkViewCreatedOnce {
61+
if (self.viewCreated) {
62+
abort();
63+
}
64+
self.viewCreated = YES;
65+
}
66+
5367
- (void)dealloc {
5468
[_view release];
5569
_view = nil;
@@ -107,6 +121,60 @@ @interface FlutterPlatformViewsTest : XCTestCase
107121

108122
@implementation FlutterPlatformViewsTest
109123

124+
- (void)testFlutterViewOnlyCreateOnceInOneFrame {
125+
flutter::FlutterPlatformViewsTestMockPlatformViewDelegate mock_delegate;
126+
auto thread_task_runner = CreateNewThread("FlutterPlatformViewsTest");
127+
flutter::TaskRunners runners(/*label=*/self.name.UTF8String,
128+
/*platform=*/thread_task_runner,
129+
/*raster=*/thread_task_runner,
130+
/*ui=*/thread_task_runner,
131+
/*io=*/thread_task_runner);
132+
auto flutterPlatformViewsController = std::make_shared<flutter::FlutterPlatformViewsController>();
133+
auto platform_view = std::make_unique<flutter::PlatformViewIOS>(
134+
/*delegate=*/mock_delegate,
135+
/*rendering_api=*/flutter::IOSRenderingAPI::kSoftware,
136+
/*platform_views_controller=*/flutterPlatformViewsController,
137+
/*task_runners=*/runners);
138+
139+
FlutterPlatformViewsTestMockFlutterPlatformFactory* factory =
140+
[[FlutterPlatformViewsTestMockFlutterPlatformFactory new] autorelease];
141+
flutterPlatformViewsController->RegisterViewFactory(
142+
factory, @"MockFlutterPlatformView",
143+
FlutterPlatformViewGestureRecognizersBlockingPolicyEager);
144+
FlutterResult result = ^(id result) {
145+
};
146+
flutterPlatformViewsController->OnMethodCall(
147+
[FlutterMethodCall
148+
methodCallWithMethodName:@"create"
149+
arguments:@{@"id" : @2, @"viewType" : @"MockFlutterPlatformView"}],
150+
result);
151+
UIView* mockFlutterView = [[[UIView alloc] initWithFrame:CGRectMake(0, 0, 500, 500)] autorelease];
152+
flutterPlatformViewsController->SetFlutterView(mockFlutterView);
153+
// Create embedded view params
154+
flutter::MutatorsStack stack;
155+
// Layer tree always pushes a screen scale factor to the stack
156+
SkMatrix screenScaleMatrix =
157+
SkMatrix::MakeScale([UIScreen mainScreen].scale, [UIScreen mainScreen].scale);
158+
stack.PushTransform(screenScaleMatrix);
159+
// Push a translate matrix
160+
SkMatrix translateMatrix = SkMatrix::MakeTrans(100, 100);
161+
stack.PushTransform(translateMatrix);
162+
SkMatrix finalMatrix;
163+
finalMatrix.setConcat(screenScaleMatrix, translateMatrix);
164+
165+
auto embeddedViewParams =
166+
std::make_unique<flutter::EmbeddedViewParams>(finalMatrix, SkSize::Make(300, 300), stack);
167+
168+
flutterPlatformViewsController->PrerollCompositeEmbeddedView(2, std::move(embeddedViewParams));
169+
flutterPlatformViewsController->CompositeEmbeddedView(2);
170+
171+
flutterPlatformViewsController->GetPlatformViewRect(2);
172+
173+
XCTAssertNotNil(gMockPlatformView);
174+
175+
flutterPlatformViewsController->Reset();
176+
}
177+
110178
- (void)testCanCreatePlatformViewWithoutFlutterView {
111179
flutter::FlutterPlatformViewsTestMockPlatformViewDelegate mock_delegate;
112180
auto thread_task_runner = CreateNewThread("FlutterPlatformViewsTest");

shell/platform/darwin/ios/framework/Source/FlutterPlatformViews_Internal.h

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -150,12 +150,12 @@ class FlutterPlatformViewsController {
150150
void PrerollCompositeEmbeddedView(int view_id,
151151
std::unique_ptr<flutter::EmbeddedViewParams> params);
152152

153-
// Returns the `FlutterPlatformView` object associated with the view_id.
153+
// Returns the `FlutterPlatformView`'s `view` object associated with the view_id.
154154
//
155155
// If the `FlutterPlatformViewsController` does not contain any `FlutterPlatformView` object or
156156
// a `FlutterPlatformView` object asscociated with the view_id cannot be found, the method
157157
// returns nil.
158-
NSObject<FlutterPlatformView>* GetPlatformViewByID(int view_id);
158+
UIView* GetPlatformViewByID(int view_id);
159159

160160
PostPrerollResult PostPrerollAction(fml::RefPtr<fml::RasterThreadMerger> raster_thread_merger);
161161

@@ -331,6 +331,9 @@ class FlutterPlatformViewsController {
331331

332332
// Prevent the touch sequence from ever arriving to the embedded view.
333333
- (void)blockGesture;
334+
335+
// Get embedded view
336+
- (UIView*)embeddedView;
334337
@end
335338

336339
#endif // FLUTTER_SHELL_PLATFORM_DARWIN_IOS_FRAMEWORK_SOURCE_FLUTTERPLATFORMVIEWS_INTERNAL_H_

shell/platform/darwin/ios/framework/Source/SemanticsObject.mm

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -559,7 +559,7 @@ - (instancetype)initWithSemanticsObject:(SemanticsObject*)object {
559559
_semanticsObject = object;
560560
auto controller = object.bridge->GetPlatformViewsController();
561561
if (controller) {
562-
_platformView = [[controller->GetPlatformViewByID(object.node.platformViewId) view] retain];
562+
_platformView = [controller->GetPlatformViewByID(object.node.platformViewId) retain];
563563
}
564564
}
565565
return self;

testing/scenario_app/ios/Scenarios/Scenarios/TextPlatformView.m

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -67,6 +67,7 @@ @interface TextPlatformView () <TestGestureRecognizerDelegate>
6767
@implementation TextPlatformView {
6868
UITextView* _textView;
6969
FlutterMethodChannel* _channel;
70+
BOOL _viewCreated;
7071
}
7172

7273
- (instancetype)initWithFrame:(CGRect)frame
@@ -87,11 +88,18 @@ - (instancetype)initWithFrame:(CGRect)frame
8788
[_textView addGestureRecognizer:gestureRecognizer];
8889
gestureRecognizer.testTapGestureRecognizerDelegate = self;
8990
_textView.accessibilityLabel = @"";
91+
92+
_viewCreated = NO;
9093
}
9194
return self;
9295
}
9396

9497
- (UIView*)view {
98+
// Makes sure the engine only calls this method once.
99+
if (_viewCreated) {
100+
abort();
101+
}
102+
_viewCreated = YES;
95103
return _textView;
96104
}
97105

0 commit comments

Comments
 (0)