mirror of
https://github.com/flutter/flutter.git
synced 2026-02-20 02:29:02 +08:00
Revert "[ios] Dynamic Content Resizing" (#178958)
Reverts flutter/flutter#177410 The change is causing the task `wide_gamut_ios` to hang every time. Local testing shows that reverting this commit fixes the problem.
This commit is contained in:
parent
12b5c1cfdf
commit
c8cfb2b1a5
@ -54,9 +54,6 @@ Future<void> main() async {
|
||||
final File marquee = File(path.join(flutterModuleLibSource.path, 'marquee'));
|
||||
marquee.copySync(path.join(flutterModuleLibDestination.path, 'marquee.dart'));
|
||||
|
||||
final File resize = File(path.join(flutterModuleLibSource.path, 'resize'));
|
||||
resize.copySync(path.join(flutterModuleLibDestination.path, 'resize.dart'));
|
||||
|
||||
section('Create package with native assets');
|
||||
|
||||
const String ffiPackageName = 'ffi_package';
|
||||
|
||||
@ -126,30 +126,6 @@ static const CGFloat kStandardTimeOut = 60.0;
|
||||
XCTAssertTrue([app.navigationBars[@"Flutter iOS Demos Home"] waitForExistenceWithTimeout:kStandardTimeOut]);
|
||||
}
|
||||
|
||||
- (void)testResize {
|
||||
XCUIApplication *app = self.app;
|
||||
|
||||
[self waitForAndTapElement:app.buttons[@"Dynamic Content Resizing"]];
|
||||
XCUIElement *flutterView = app.otherElements[@"flutter_view"];
|
||||
|
||||
CGRect flutterViewFrame = flutterView.frame;
|
||||
CGSize flutterViewSize = flutterViewFrame.size;
|
||||
|
||||
XCTAssertTrue(flutterViewSize.height == 100);
|
||||
|
||||
[self waitForAndTapElement: app.otherElements[@"flutter_view"]];
|
||||
|
||||
XCUIElement *flutterViewPostClick = app.otherElements[@"flutter_view"];
|
||||
CGRect flutterViewFramePostClick = flutterViewPostClick.frame;
|
||||
CGSize flutterViewSizePostClick = flutterViewFramePostClick.size;
|
||||
|
||||
XCTAssertTrue(flutterViewSizePostClick.height == 200);
|
||||
|
||||
// Back navigation.
|
||||
[app.navigationBars[@"Dynamic Content Resizing"].buttons[@"Flutter iOS Demos Home"] tap];
|
||||
XCTAssertTrue([app.navigationBars[@"Flutter iOS Demos Home"] waitForExistenceWithTimeout:kStandardTimeOut]);
|
||||
}
|
||||
|
||||
- (void)waitForAndTapElement:(XCUIElement *)element {
|
||||
NSPredicate *hittable = [NSPredicate predicateWithFormat:@"exists == YES AND hittable == YES"];
|
||||
[self expectationForPredicate:hittable evaluatedWithObject:element handler:nil];
|
||||
|
||||
@ -13,7 +13,6 @@
|
||||
74F97871215AB9E9005A0F04 /* LaunchScreen.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 74F9786F215AB9E9005A0F04 /* LaunchScreen.storyboard */; };
|
||||
74F97874215AB9E9005A0F04 /* main.m in Sources */ = {isa = PBXBuildFile; fileRef = 74F97873215AB9E9005A0F04 /* main.m */; };
|
||||
7E06ECD9FAEFCA4A0ED04D6E /* libPods-FlutterUITests.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 327E8CC22A494E7D7155FB58 /* libPods-FlutterUITests.a */; };
|
||||
F2D997DE2EA8BCB0002FC7EC /* DynamicResizingViewController.m in Sources */ = {isa = PBXBuildFile; fileRef = F2D997DD2EA8BCB0002FC7EC /* DynamicResizingViewController.m */; };
|
||||
F7C2661424AC18D00085742D /* DualFlutterViewController.m in Sources */ = {isa = PBXBuildFile; fileRef = F7C2660E24AC18D00085742D /* DualFlutterViewController.m */; };
|
||||
F7C2661524AC18D00085742D /* FullScreenViewController.m in Sources */ = {isa = PBXBuildFile; fileRef = F7C2660F24AC18D00085742D /* FullScreenViewController.m */; };
|
||||
F7C2661624AC18D00085742D /* HybridViewController.m in Sources */ = {isa = PBXBuildFile; fileRef = F7C2661124AC18D00085742D /* HybridViewController.m */; };
|
||||
@ -46,8 +45,6 @@
|
||||
8E6751F293967EE3DFF4178A /* Pods-FlutterUITests.release.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-FlutterUITests.release.xcconfig"; path = "Pods/Target Support Files/Pods-FlutterUITests/Pods-FlutterUITests.release.xcconfig"; sourceTree = "<group>"; };
|
||||
91B7D4263BFD246A27391225 /* Pods-Host.debug.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-Host.debug.xcconfig"; path = "Pods/Target Support Files/Pods-Host/Pods-Host.debug.xcconfig"; sourceTree = "<group>"; };
|
||||
E20960BD8B505D605FE82853 /* libPods-Host.a */ = {isa = PBXFileReference; explicitFileType = archive.ar; includeInIndex = 0; path = "libPods-Host.a"; sourceTree = BUILT_PRODUCTS_DIR; };
|
||||
F2D997DC2EA8BCB0002FC7EC /* DynamicResizingViewController.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = DynamicResizingViewController.h; sourceTree = "<group>"; };
|
||||
F2D997DD2EA8BCB0002FC7EC /* DynamicResizingViewController.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = DynamicResizingViewController.m; sourceTree = "<group>"; };
|
||||
F7C2660E24AC18D00085742D /* DualFlutterViewController.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = DualFlutterViewController.m; sourceTree = "<group>"; };
|
||||
F7C2660F24AC18D00085742D /* FullScreenViewController.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = FullScreenViewController.m; sourceTree = "<group>"; };
|
||||
F7C2661024AC18D00085742D /* HybridViewController.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = HybridViewController.h; sourceTree = "<group>"; };
|
||||
@ -106,8 +103,6 @@
|
||||
74F97863215AB9E8005A0F04 /* Host */ = {
|
||||
isa = PBXGroup;
|
||||
children = (
|
||||
F2D997DC2EA8BCB0002FC7EC /* DynamicResizingViewController.h */,
|
||||
F2D997DD2EA8BCB0002FC7EC /* DynamicResizingViewController.m */,
|
||||
F7C2661724AC18DD0085742D /* MainViewController.h */,
|
||||
F7C2661A24AC18DD0085742D /* MainViewController.m */,
|
||||
F7C2661924AC18DD0085742D /* NativeViewController.h */,
|
||||
@ -289,14 +284,10 @@
|
||||
inputFileListPaths = (
|
||||
"${PODS_ROOT}/Target Support Files/Pods-FlutterUITests/Pods-FlutterUITests-frameworks-${CONFIGURATION}-input-files.xcfilelist",
|
||||
);
|
||||
inputPaths = (
|
||||
);
|
||||
name = "[CP] Embed Pods Frameworks";
|
||||
outputFileListPaths = (
|
||||
"${PODS_ROOT}/Target Support Files/Pods-FlutterUITests/Pods-FlutterUITests-frameworks-${CONFIGURATION}-output-files.xcfilelist",
|
||||
);
|
||||
outputPaths = (
|
||||
);
|
||||
runOnlyForDeploymentPostprocessing = 0;
|
||||
shellPath = /bin/sh;
|
||||
shellScript = "\"${PODS_ROOT}/Target Support Files/Pods-FlutterUITests/Pods-FlutterUITests-frameworks.sh\"\n";
|
||||
@ -314,8 +305,6 @@
|
||||
"${SRCROOT}/../hello/.ios/Flutter/flutter_export_environment.sh",
|
||||
);
|
||||
name = "[CP-User] Run Flutter Build hello Script";
|
||||
outputPaths = (
|
||||
);
|
||||
runOnlyForDeploymentPostprocessing = 0;
|
||||
shellPath = /bin/sh;
|
||||
shellScript = "set -e\nset -u\nsource \"${SRCROOT}/../hello/.ios/Flutter/flutter_export_environment.sh\"\n\"$FLUTTER_ROOT\"/packages/flutter_tools/bin/xcode_backend.sh build";
|
||||
@ -328,14 +317,10 @@
|
||||
inputFileListPaths = (
|
||||
"${PODS_ROOT}/Target Support Files/Pods-Host/Pods-Host-frameworks-${CONFIGURATION}-input-files.xcfilelist",
|
||||
);
|
||||
inputPaths = (
|
||||
);
|
||||
name = "[CP] Embed Pods Frameworks";
|
||||
outputFileListPaths = (
|
||||
"${PODS_ROOT}/Target Support Files/Pods-Host/Pods-Host-frameworks-${CONFIGURATION}-output-files.xcfilelist",
|
||||
);
|
||||
outputPaths = (
|
||||
);
|
||||
runOnlyForDeploymentPostprocessing = 0;
|
||||
shellPath = /bin/sh;
|
||||
shellScript = "\"${PODS_ROOT}/Target Support Files/Pods-Host/Pods-Host-frameworks.sh\"\n";
|
||||
@ -353,8 +338,6 @@
|
||||
"${SRCROOT}/../hello/.ios/Flutter/flutter_export_environment.sh",
|
||||
);
|
||||
name = "[CP-User] Run Flutter Build hello Script";
|
||||
outputPaths = (
|
||||
);
|
||||
runOnlyForDeploymentPostprocessing = 0;
|
||||
shellPath = /bin/sh;
|
||||
shellScript = "set -e\nset -u\nsource \"${SRCROOT}/../hello/.ios/Flutter/flutter_export_environment.sh\"\n\"$FLUTTER_ROOT\"/packages/flutter_tools/bin/xcode_backend.sh build";
|
||||
@ -389,7 +372,6 @@
|
||||
buildActionMask = 2147483647;
|
||||
files = (
|
||||
F7C2661524AC18D00085742D /* FullScreenViewController.m in Sources */,
|
||||
F2D997DE2EA8BCB0002FC7EC /* DynamicResizingViewController.m in Sources */,
|
||||
F7C2661424AC18D00085742D /* DualFlutterViewController.m in Sources */,
|
||||
F7C2661C24AC18DD0085742D /* MainViewController.m in Sources */,
|
||||
74F97874215AB9E9005A0F04 /* main.m in Sources */,
|
||||
|
||||
@ -1,16 +0,0 @@
|
||||
// Copyright 2014 The Flutter Authors. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style license that can be
|
||||
// found in the LICENSE file.
|
||||
|
||||
#import <UIKit/UIKit.h>
|
||||
#import <Flutter/Flutter.h>
|
||||
|
||||
NS_ASSUME_NONNULL_BEGIN
|
||||
|
||||
@interface DynamicResizingViewController : UIViewController
|
||||
|
||||
@property (readonly, strong, nonatomic) FlutterViewController* flutterViewController;
|
||||
|
||||
@end
|
||||
|
||||
NS_ASSUME_NONNULL_END
|
||||
@ -1,72 +0,0 @@
|
||||
// Copyright 2014 The Flutter Authors. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style license that can be
|
||||
// found in the LICENSE file.
|
||||
|
||||
#import <Flutter/Flutter.h>
|
||||
|
||||
#import "DynamicResizingViewController.h"
|
||||
|
||||
@interface DynamicResizingViewController ()
|
||||
|
||||
@end
|
||||
|
||||
@implementation DynamicResizingViewController {
|
||||
FlutterBasicMessageChannel *_messageChannel;
|
||||
}
|
||||
|
||||
- (void)viewDidLoad {
|
||||
[super viewDidLoad];
|
||||
|
||||
self.view.backgroundColor = [UIColor systemBackgroundColor];
|
||||
self.title = @"Dynamic Content Resizing";
|
||||
|
||||
UIScrollView *scrollView = [[UIScrollView alloc] init];
|
||||
scrollView.translatesAutoresizingMaskIntoConstraints = NO;
|
||||
[self.view addSubview:scrollView];
|
||||
|
||||
UIStackView *stackView = [[UIStackView alloc] init];
|
||||
stackView.translatesAutoresizingMaskIntoConstraints = NO;
|
||||
stackView.axis = UILayoutConstraintAxisVertical;
|
||||
stackView.spacing = 10;
|
||||
[scrollView addSubview:stackView];
|
||||
|
||||
[NSLayoutConstraint activateConstraints:@[
|
||||
[scrollView.topAnchor constraintEqualToAnchor:self.view.safeAreaLayoutGuide.topAnchor],
|
||||
[scrollView.leadingAnchor constraintEqualToAnchor:self.view.leadingAnchor],
|
||||
[scrollView.trailingAnchor constraintEqualToAnchor:self.view.trailingAnchor],
|
||||
[scrollView.bottomAnchor constraintEqualToAnchor:self.view.safeAreaLayoutGuide.bottomAnchor]
|
||||
]];
|
||||
|
||||
[NSLayoutConstraint activateConstraints:@[
|
||||
[stackView.topAnchor constraintEqualToAnchor:scrollView.contentLayoutGuide.topAnchor],
|
||||
[stackView.leadingAnchor constraintEqualToAnchor:scrollView.contentLayoutGuide.leadingAnchor],
|
||||
[stackView.trailingAnchor constraintEqualToAnchor:scrollView.contentLayoutGuide.trailingAnchor],
|
||||
[stackView.bottomAnchor constraintEqualToAnchor:scrollView.contentLayoutGuide.bottomAnchor],
|
||||
|
||||
[stackView.widthAnchor constraintEqualToAnchor:scrollView.frameLayoutGuide.widthAnchor]
|
||||
]];
|
||||
|
||||
for (int index = 1; index <= 50; index++) {
|
||||
if (index == 10) {
|
||||
_flutterViewController = [[FlutterViewController alloc] init];
|
||||
[_flutterViewController setInitialRoute:@"resize"];
|
||||
_flutterViewController.autoResizable = true;
|
||||
[self addChildViewController:_flutterViewController];
|
||||
[stackView addArrangedSubview:_flutterViewController.view];
|
||||
_flutterViewController.view.translatesAutoresizingMaskIntoConstraints = NO;
|
||||
_flutterViewController.view.accessibilityIdentifier = @"flutter_view";
|
||||
[_flutterViewController didMoveToParentViewController:self];
|
||||
} else {
|
||||
UILabel *label = [[UILabel alloc] init];
|
||||
label.text = [NSString stringWithFormat:@" Hello from iOS %d ", index];
|
||||
label.backgroundColor = (index % 2 == 0) ? [UIColor systemGray5Color] : [UIColor systemGray3Color];
|
||||
label.translatesAutoresizingMaskIntoConstraints = NO;
|
||||
|
||||
[label.heightAnchor constraintEqualToConstant:44].active = YES;
|
||||
|
||||
[stackView addArrangedSubview:label];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@end
|
||||
@ -9,7 +9,6 @@
|
||||
#import "FullScreenViewController.h"
|
||||
#import "HybridViewController.h"
|
||||
#import "NativeViewController.h"
|
||||
#import "DynamicResizingViewController.h"
|
||||
|
||||
@interface MainViewController ()
|
||||
|
||||
@ -45,7 +44,6 @@
|
||||
self.flutterViewWarmButton = [self addButton:@"Flutter View (Warm)" action:@selector(showFlutterViewWarm)];
|
||||
[self addButton:@"Hybrid View (Warm)" action:@selector(showHybridView)];
|
||||
[self addButton:@"Dual Flutter View (Cold)" action:@selector(showDualView)];
|
||||
[self addButton:@"Dynamic Content Resizing" action:@selector(showContentResizingView)];
|
||||
}
|
||||
|
||||
- (FlutterEngine *)engine {
|
||||
@ -76,13 +74,6 @@
|
||||
animated:YES];
|
||||
}
|
||||
|
||||
- (void)showContentResizingView {
|
||||
DynamicResizingViewController *resizingViewController =
|
||||
[[DynamicResizingViewController alloc] init];
|
||||
[self.navigationController pushViewController:resizingViewController
|
||||
animated:NO];
|
||||
}
|
||||
|
||||
- (void)showFullScreenCold {
|
||||
FullScreenViewController *flutterViewController =
|
||||
[[FullScreenViewController alloc] init];
|
||||
|
||||
@ -10,7 +10,6 @@ import 'package:flutter/services.dart';
|
||||
import 'package:ffi_package/ffi_package.dart';
|
||||
|
||||
import 'marquee.dart';
|
||||
import 'resize.dart';
|
||||
|
||||
/// Route names. (See [main] for more details.)
|
||||
///
|
||||
@ -19,7 +18,6 @@ const String greenMarqueeRouteName = 'marquee_green';
|
||||
const String purpleMarqueeRouteName = 'marquee_purple';
|
||||
const String fullscreenRouteName = 'full';
|
||||
const String hybridRouteName = 'hybrid';
|
||||
const String resizeRouteName = 'resize';
|
||||
|
||||
/// Channel used to let the Flutter app know to reset the app to a specific
|
||||
/// route. See the [run] method.
|
||||
@ -57,9 +55,6 @@ Future<String> run(String? name) async {
|
||||
case purpleMarqueeRouteName:
|
||||
runApp(Marquee(color: Colors.purple[400]));
|
||||
break;
|
||||
case resizeRouteName:
|
||||
runApp(ResizeApp());
|
||||
break;
|
||||
case fullscreenRouteName:
|
||||
case hybridRouteName:
|
||||
default:
|
||||
|
||||
@ -1,42 +0,0 @@
|
||||
// Copyright 2014 The Flutter Authors. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style license that can be
|
||||
// found in the LICENSE file.
|
||||
|
||||
import 'package:flutter/material.dart';
|
||||
|
||||
class ResizeApp extends StatefulWidget {
|
||||
/// Creates the [ResizeApp].
|
||||
const ResizeApp({super.key});
|
||||
|
||||
@override
|
||||
State<ResizeApp> createState() => _ResizeAppState();
|
||||
}
|
||||
|
||||
class _ResizeAppState extends State<ResizeApp> {
|
||||
int _listSize = 1;
|
||||
void _addToList() {
|
||||
setState(() {
|
||||
_listSize++;
|
||||
});
|
||||
}
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return GestureDetector(
|
||||
onTap: _addToList, // The tap anywhere logic
|
||||
child: Center(
|
||||
heightFactor: 1,
|
||||
child: Directionality(
|
||||
textDirection: TextDirection.ltr,
|
||||
child: Column(
|
||||
mainAxisSize: MainAxisSize.min,
|
||||
children: <Widget>[
|
||||
for (int i = 0; i < _listSize; i++)
|
||||
Container(color: HSVColor.fromAHSV(1, (10.0 * i), 1, 1).toColor(), height: 100),
|
||||
],
|
||||
),
|
||||
),
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
@ -13,7 +13,6 @@
|
||||
#include "flutter/fml/message_loop.h"
|
||||
#include "flutter/fml/message_loop_impl.h"
|
||||
#include "flutter/fml/message_loop_task_queues.h"
|
||||
#include "flutter/fml/synchronization/waitable_event.h"
|
||||
|
||||
namespace fml {
|
||||
|
||||
|
||||
@ -254,19 +254,6 @@ FLUTTER_DARWIN_EXPORT
|
||||
*/
|
||||
@property(nonatomic, readonly) BOOL engineAllowHeadlessExecution;
|
||||
|
||||
/**
|
||||
* Controls whether the created view can be sized based on its content.
|
||||
* When set to `YES`, the FlutterView will be the same size as the outermost widget.
|
||||
* Cannot be used with unbounded height widgets, such as Scaffold.
|
||||
* This property is intended to be used with Add-to-App scenarios.
|
||||
*
|
||||
* Once auto resizing is enabled, the FlutterView will rely on custom constraints from then on.
|
||||
* Avoid disabling it after enabling, as behaviour will then be undefined.
|
||||
*
|
||||
* Default is `NO`.
|
||||
*/
|
||||
@property(nonatomic, getter=isAutoResizable) BOOL autoResizable;
|
||||
|
||||
@end
|
||||
|
||||
NS_ASSUME_NONNULL_END
|
||||
|
||||
@ -404,14 +404,6 @@ extern CFTimeInterval display_link_target;
|
||||
}
|
||||
|
||||
- (void)presentOnMainThread:(FlutterTexture*)texture {
|
||||
if (texture.texture.width != _drawableSize.width ||
|
||||
texture.texture.height != _drawableSize.height) {
|
||||
// This texture was created with an old size, but the view has since been
|
||||
// resized. Do not present this stale frame to avoid distortion. The texture
|
||||
// will be correctly recycled on the next frame.
|
||||
return;
|
||||
}
|
||||
|
||||
// This is needed otherwise frame gets skipped on touch begin / end. Go figure.
|
||||
// Might also be placebo
|
||||
[self setNeedsDisplay];
|
||||
|
||||
@ -665,19 +665,8 @@ static CGRect GetCGRectFromDlRect(const DlRect& clipDlRect) {
|
||||
withIosContext:(const std::shared_ptr<flutter::IOSContext>&)iosContext {
|
||||
TRACE_EVENT0("flutter", "PlatformViewsController::SubmitFrame");
|
||||
|
||||
// No platform views to render.
|
||||
// No platform views to render; we're done.
|
||||
if (self.flutterView == nil || (self.compositionOrder.empty() && !self.hadPlatformViews)) {
|
||||
// No platform views to render but the FlutterView may need to be resized.
|
||||
if (self.flutterView != nil) {
|
||||
if (self.platformTaskRunner->RunsTasksOnCurrentThread()) {
|
||||
[self performResize:self.frameSize];
|
||||
} else {
|
||||
PostTaskSync(self.platformTaskRunner, [self, frameSize = self.frameSize]() mutable {
|
||||
[self performResize:frameSize];
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
self.hadPlatformViews = NO;
|
||||
return background_frame->Submit();
|
||||
}
|
||||
@ -763,13 +752,15 @@ static CGRect GetCGRectFromDlRect(const DlRect& clipDlRect) {
|
||||
std::vector<std::shared_ptr<flutter::OverlayLayer>> unusedLayers =
|
||||
self.layerPool->RemoveUnusedLayers();
|
||||
self.layerPool->RecycleLayers();
|
||||
|
||||
auto task = [self, //
|
||||
platformViewLayers = std::move(platformViewLayers), //
|
||||
currentCompositionParams = self.currentCompositionParams, //
|
||||
viewsToRecomposite = self.viewsToRecomposite, //
|
||||
compositionOrder = self.compositionOrder, //
|
||||
unusedLayers = std::move(unusedLayers), //
|
||||
surfaceFrames = std::move(surfaceFrames)]() mutable {
|
||||
surfaceFrames = std::move(surfaceFrames) //
|
||||
]() mutable {
|
||||
[self performSubmit:platformViewLayers
|
||||
currentCompositionParams:currentCompositionParams
|
||||
viewsToRecomposite:viewsToRecomposite
|
||||
@ -779,6 +770,7 @@ static CGRect GetCGRectFromDlRect(const DlRect& clipDlRect) {
|
||||
};
|
||||
|
||||
fml::TaskRunner::RunNowOrPostTask(self.platformTaskRunner, fml::MakeCopyable(std::move(task)));
|
||||
|
||||
return didEncode;
|
||||
}
|
||||
|
||||
@ -807,16 +799,6 @@ static CGRect GetCGRectFromDlRect(const DlRect& clipDlRect) {
|
||||
}
|
||||
}
|
||||
|
||||
- (void)performResize:(const flutter::DlISize&)frameSize {
|
||||
TRACE_EVENT0("flutter", "PlatformViewsController::PerformResize");
|
||||
FML_DCHECK([[NSThread currentThread] isMainThread]);
|
||||
|
||||
if (self.flutterView != nil) {
|
||||
[(FlutterView*)self.flutterView
|
||||
setIntrinsicContentSize:CGSizeMake(frameSize.width, frameSize.height)];
|
||||
}
|
||||
}
|
||||
|
||||
- (void)performSubmit:(const LayersMap&)platformViewLayers
|
||||
currentCompositionParams:
|
||||
(std::unordered_map<int64_t, flutter::EmbeddedViewParams>&)currentCompositionParams
|
||||
@ -1019,18 +1001,4 @@ static CGRect GetCGRectFromDlRect(const DlRect& clipDlRect) {
|
||||
return _previousCompositionOrder;
|
||||
}
|
||||
|
||||
namespace {
|
||||
void PostTaskSync(const fml::RefPtr<fml::TaskRunner>& task_runner, fml::closure task) {
|
||||
FML_DCHECK(!task_runner->RunsTasksOnCurrentThread());
|
||||
fml::AutoResetWaitableEvent latch;
|
||||
task_runner->PostTask([&latch, task = std::move(task)]() {
|
||||
if (task) {
|
||||
task();
|
||||
}
|
||||
latch.Signal();
|
||||
});
|
||||
latch.Wait();
|
||||
}
|
||||
} // namespace
|
||||
|
||||
@end
|
||||
|
||||
@ -29,23 +29,6 @@
|
||||
- (void)flutterViewAccessibilityDidCall;
|
||||
@end
|
||||
|
||||
/**
|
||||
* A custom NSLayoutConstraint subclass for autoresizing the FlutterView.
|
||||
* This class is a special NSLayoutConstraint used internally to
|
||||
* manage the dynamic resizing of a FlutterView based on its content.
|
||||
*
|
||||
* In native, `intrinsicContentSize` is a public property that determines the preferred
|
||||
* sized of an UIView, based on it's internal content. Given a position and layout constraints,
|
||||
* this allows the UIView to size itself.
|
||||
* However, the mechanism in which this sizing occurs based on`intrinsicContentSize`
|
||||
* and the layout constraints is private.
|
||||
*
|
||||
* This custom NSLayoutConstraint allows us to replicate this mechanizm without needing to rely
|
||||
* on private APIs.
|
||||
*/
|
||||
@interface FlutterAutoResizeLayoutConstraint : NSLayoutConstraint
|
||||
@end
|
||||
|
||||
@interface FlutterView : UIView
|
||||
|
||||
- (instancetype)init NS_UNAVAILABLE;
|
||||
@ -60,19 +43,6 @@
|
||||
- (UIScreen*)screen;
|
||||
- (MTLPixelFormat)pixelFormat;
|
||||
|
||||
/**
|
||||
* A method that sets the instrinsic content size
|
||||
* This is used when autoResizable is enabled.
|
||||
*/
|
||||
- (void)setIntrinsicContentSize:(CGSize)size;
|
||||
|
||||
/**
|
||||
* A method that resets and recalculates the instrinsic content size
|
||||
* Currently called when the device orientation changes.
|
||||
*/
|
||||
- (void)resetIntrinsicContentSize;
|
||||
@property(nonatomic, assign, readwrite) BOOL autoResizable;
|
||||
|
||||
@end
|
||||
|
||||
#endif // FLUTTER_SHELL_PLATFORM_DARWIN_IOS_FRAMEWORK_SOURCE_FLUTTERVIEW_H_
|
||||
|
||||
@ -16,12 +16,8 @@ FLUTTER_ASSERT_ARC
|
||||
@property(nonatomic, weak) UIWindowScene* previousScene;
|
||||
@end
|
||||
|
||||
@implementation FlutterAutoResizeLayoutConstraint
|
||||
@end
|
||||
|
||||
@implementation FlutterView {
|
||||
BOOL _isWideGamutEnabled;
|
||||
CGSize _intrinsicSize;
|
||||
}
|
||||
|
||||
- (instancetype)init {
|
||||
@ -43,70 +39,6 @@ FLUTTER_ASSERT_ARC
|
||||
return self.window.windowScene.screen;
|
||||
}
|
||||
|
||||
// iOS has a concept of "intrinsicContentSize", which indicates the size a view would like to be
|
||||
// based on its content. When an intrinsicContentSize is set, iOS will automatically add Auto Layout
|
||||
// constraints for the width and/or height. However, the constraints use a private API. There are
|
||||
// situations where we may want to filter these constraints. To avoid using a private API, Flutter
|
||||
// creates a custom constraint called FlutterAutoResizeLayoutConstraint to add a width/height
|
||||
// constraint that reflects the intrinsicContentSize.
|
||||
- (void)setIntrinsicContentSize:(CGSize)size {
|
||||
if (!self.autoResizable) {
|
||||
return;
|
||||
}
|
||||
|
||||
CGFloat scale = self.window.windowScene.screen.scale;
|
||||
CGSize scaledSize = CGSizeMake(size.width / scale, size.height / scale);
|
||||
|
||||
CGSize roundedScaleSize = CGSizeMake(roundf(scaledSize.width), roundf(scaledSize.height));
|
||||
CGSize roundedIntrinsicSize =
|
||||
CGSizeMake(roundf(_intrinsicSize.width), roundf(_intrinsicSize.height));
|
||||
|
||||
// If the size has not changed, don't update constraints.
|
||||
if (CGSizeEqualToSize(roundedIntrinsicSize, roundedScaleSize)) {
|
||||
return;
|
||||
}
|
||||
_intrinsicSize = scaledSize;
|
||||
|
||||
self.translatesAutoresizingMaskIntoConstraints = false;
|
||||
|
||||
// Remove any existing FlutterAutoResizeLayoutConstraint
|
||||
[self removeAutoResizeLayoutConstraints];
|
||||
|
||||
FlutterAutoResizeLayoutConstraint* widthConstraint =
|
||||
[FlutterAutoResizeLayoutConstraint constraintWithItem:self
|
||||
attribute:NSLayoutAttributeWidth
|
||||
relatedBy:NSLayoutRelationEqual
|
||||
toItem:nil
|
||||
attribute:NSLayoutAttributeNotAnAttribute
|
||||
multiplier:1.0
|
||||
constant:scaledSize.width];
|
||||
|
||||
FlutterAutoResizeLayoutConstraint* heightConstraint =
|
||||
[FlutterAutoResizeLayoutConstraint constraintWithItem:self
|
||||
attribute:NSLayoutAttributeHeight
|
||||
relatedBy:NSLayoutRelationEqual
|
||||
toItem:nil
|
||||
attribute:NSLayoutAttributeNotAnAttribute
|
||||
multiplier:1.0
|
||||
constant:scaledSize.height];
|
||||
|
||||
[NSLayoutConstraint activateConstraints:@[ widthConstraint, heightConstraint ]];
|
||||
[self setNeedsLayout];
|
||||
}
|
||||
|
||||
- (void)resetIntrinsicContentSize {
|
||||
_intrinsicSize = CGSizeMake(UIViewNoIntrinsicMetric, UIViewNoIntrinsicMetric);
|
||||
[self removeAutoResizeLayoutConstraints];
|
||||
}
|
||||
|
||||
- (void)removeAutoResizeLayoutConstraints {
|
||||
for (NSLayoutConstraint* constraint in self.constraints) {
|
||||
if ([constraint isKindOfClass:[FlutterAutoResizeLayoutConstraint class]]) {
|
||||
constraint.active = NO;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
- (MTLPixelFormat)pixelFormat {
|
||||
if ([self.layer isKindOfClass:[CAMetalLayer class]]) {
|
||||
// It is a known Apple bug that CAMetalLayer incorrectly reports its supported
|
||||
@ -147,8 +79,6 @@ FLUTTER_ASSERT_ARC
|
||||
_delegate = delegate;
|
||||
_isWideGamutEnabled = isWideGamutEnabled;
|
||||
self.layer.opaque = opaque;
|
||||
_autoResizable = NO;
|
||||
_intrinsicSize = CGSizeMake(UIViewNoIntrinsicMetric, UIViewNoIntrinsicMetric);
|
||||
}
|
||||
|
||||
return self;
|
||||
|
||||
@ -101,6 +101,7 @@ typedef struct MouseState {
|
||||
* Whether we should ignore viewport metrics updates during rotation transition.
|
||||
*/
|
||||
@property(nonatomic, assign) BOOL shouldIgnoreViewportMetricsUpdatesDuringRotation;
|
||||
|
||||
/**
|
||||
* Keyboard animation properties
|
||||
*/
|
||||
@ -130,10 +131,6 @@ typedef struct MouseState {
|
||||
/// the same with frame rate of rendering.
|
||||
@property(nonatomic, strong) VSyncClient* touchRateCorrectionVSyncClient;
|
||||
|
||||
/// The size of the FlutterView's frame, as determined by auto-layout,
|
||||
/// before Flutter's custom auto-resizing constraints are applied.
|
||||
@property(nonatomic, assign) CGSize sizeBeforeAutoResized;
|
||||
|
||||
/*
|
||||
* Mouse and trackpad gesture recognizers
|
||||
*/
|
||||
@ -1461,7 +1458,6 @@ static flutter::PointerData::DeviceKind DeviceKindFromTouchType(UITouch* touch)
|
||||
bool firstViewBoundsUpdate = !_viewportMetrics.physical_width;
|
||||
_viewportMetrics.device_pixel_ratio = scale;
|
||||
[self setViewportMetricsSize];
|
||||
[self checkAndUpdateAutoResizeConstraints];
|
||||
[self setViewportMetricsPaddings];
|
||||
[self updateViewportMetricsIfNeeded];
|
||||
|
||||
@ -1490,89 +1486,6 @@ static flutter::PointerData::DeviceKind DeviceKindFromTouchType(UITouch* touch)
|
||||
}
|
||||
}
|
||||
|
||||
- (BOOL)isAutoResizable {
|
||||
return self.flutterView.autoResizable;
|
||||
}
|
||||
|
||||
- (void)setAutoResizable:(BOOL)value {
|
||||
self.flutterView.autoResizable = value;
|
||||
self.flutterView.contentMode = UIViewContentModeCenter;
|
||||
}
|
||||
|
||||
- (void)checkAndUpdateAutoResizeConstraints {
|
||||
if (!self.isAutoResizable) {
|
||||
return;
|
||||
}
|
||||
|
||||
[self updateAutoResizeConstraints];
|
||||
}
|
||||
|
||||
/**
|
||||
* Updates the FlutterAutoResizeLayoutConstraints based on the view's
|
||||
* current frame.
|
||||
*
|
||||
* This method is invoked during viewDidLayoutSubviews, at which point the
|
||||
* view has completed its subview layout and applied any existing Auto Layout
|
||||
* constraints.
|
||||
*
|
||||
* Initially, the view's frame is used to determine the maximum size allowed
|
||||
* by the native layout system. This size is then used to establish the viewport
|
||||
* constraints for the Flutter engine.
|
||||
*
|
||||
* A critical consideration is that this initial frame-based sizing is only
|
||||
* applicable if FlutterAutoResizeLayoutConstraints have not yet been applied
|
||||
* by Flutter. Once Flutter applies its own FlutterAutoResizeLayoutConstraints,
|
||||
* these constraints will subsequently dictate the view's frame.
|
||||
*
|
||||
* This interaction imposes a limitation: native layout constraints that are
|
||||
* updated after Flutter has applied its auto-resize constraints may not
|
||||
* function as expected or properly influence the FlutterView's size.
|
||||
*/
|
||||
- (void)updateAutoResizeConstraints {
|
||||
BOOL hasBeenAutoResized = NO;
|
||||
for (NSLayoutConstraint* constraint in self.view.constraints) {
|
||||
if ([constraint isKindOfClass:[FlutterAutoResizeLayoutConstraint class]]) {
|
||||
hasBeenAutoResized = YES;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (!hasBeenAutoResized) {
|
||||
self.sizeBeforeAutoResized = self.view.frame.size;
|
||||
}
|
||||
|
||||
CGFloat maxWidth = self.sizeBeforeAutoResized.width;
|
||||
CGFloat maxHeight = self.sizeBeforeAutoResized.height;
|
||||
CGFloat minWidth = self.sizeBeforeAutoResized.width;
|
||||
CGFloat minHeight = self.sizeBeforeAutoResized.height;
|
||||
|
||||
// maxWidth or maxHeight may be 0 when the width/height are ambiguous, eg. for
|
||||
// unsized widgets
|
||||
if (maxWidth == 0) {
|
||||
maxWidth = CGFLOAT_MAX;
|
||||
[FlutterLogger
|
||||
logWarning:
|
||||
@"Warning: The outermost widget in the autoresizable Flutter view is unsized or has "
|
||||
@"ambiguous dimensions, causing the host native view's width to be 0. The autoresizing "
|
||||
@"logic is setting the viewport constraint to unbounded DBL_MAX to prevent "
|
||||
@"rendering failure. Please ensure your top-level Flutter widget has explicit "
|
||||
@"constraints (e.g., using SizedBox or Container)."];
|
||||
}
|
||||
if (maxHeight == 0) {
|
||||
maxHeight = CGFLOAT_MAX;
|
||||
[FlutterLogger
|
||||
logWarning:
|
||||
@"Warning: The outermost widget in the autoresizable Flutter view is unsized or has "
|
||||
@"ambiguous dimensions, causing the host native view's width to be 0. The autoresizing "
|
||||
@"logic is setting the viewport constraint to unbounded DBL_MAX to prevent "
|
||||
@"rendering failure. Please ensure your top-level Flutter widget has explicit "
|
||||
@"constraints (e.g., using SizedBox or Container)."];
|
||||
}
|
||||
_viewportMetrics.physical_min_width_constraint = minWidth * _viewportMetrics.device_pixel_ratio;
|
||||
_viewportMetrics.physical_max_width_constraint = maxWidth * _viewportMetrics.device_pixel_ratio;
|
||||
_viewportMetrics.physical_min_height_constraint = minHeight * _viewportMetrics.device_pixel_ratio;
|
||||
_viewportMetrics.physical_max_height_constraint = maxHeight * _viewportMetrics.device_pixel_ratio;
|
||||
}
|
||||
|
||||
- (void)viewSafeAreaInsetsDidChange {
|
||||
[self setViewportMetricsPaddings];
|
||||
[self updateViewportMetricsIfNeeded];
|
||||
@ -2282,12 +2195,6 @@ static flutter::PointerData::DeviceKind DeviceKindFromTouchType(UITouch* touch)
|
||||
- (void)traitCollectionDidChange:(UITraitCollection*)previousTraitCollection {
|
||||
[super traitCollectionDidChange:previousTraitCollection];
|
||||
[self onUserSettingsChanged:nil];
|
||||
|
||||
// Since this method can get triggered by changes in device orientation, reset and recalculate the
|
||||
// instrinsic size.
|
||||
if (self.isAutoResizable) {
|
||||
[self.flutterView resetIntrinsicContentSize];
|
||||
}
|
||||
}
|
||||
|
||||
- (void)onUserSettingsChanged:(NSNotification*)notification {
|
||||
|
||||
@ -142,8 +142,6 @@ extern NSNotificationName const FlutterViewControllerWillDealloc;
|
||||
nextAction:(void (^)())next API_AVAILABLE(ios(13.4));
|
||||
- (void)discreteScrollEvent:(UIPanGestureRecognizer*)recognizer;
|
||||
- (void)updateViewportMetricsIfNeeded;
|
||||
- (void)updateAutoResizeConstraints;
|
||||
- (void)checkAndUpdateAutoResizeConstraints;
|
||||
- (void)onUserSettingsChanged:(NSNotification*)notification;
|
||||
- (void)applicationWillTerminate:(NSNotification*)notification;
|
||||
- (void)goToApplicationLifecycle:(nonnull NSString*)state;
|
||||
@ -999,44 +997,6 @@ extern NSNotificationName const FlutterViewControllerWillDealloc;
|
||||
OCMVerifyAll(mockEngine);
|
||||
}
|
||||
|
||||
- (void)testUpdatedViewportMetricsDoesResizeFlutterViewWhenAutoResizable {
|
||||
FlutterEngine* mockEngine = OCMPartialMock([[FlutterEngine alloc] init]);
|
||||
[mockEngine createShell:@"" libraryURI:@"" initialRoute:nil];
|
||||
|
||||
FlutterViewController* realVC = [[FlutterViewController alloc] initWithEngine:mockEngine
|
||||
nibName:nil
|
||||
bundle:nil];
|
||||
id mockVC = OCMPartialMock(realVC);
|
||||
mockEngine.viewController = mockVC;
|
||||
|
||||
OCMExpect([mockVC updateAutoResizeConstraints]);
|
||||
|
||||
[mockVC setAutoResizable:YES];
|
||||
|
||||
[mockVC viewDidLayoutSubviews];
|
||||
|
||||
OCMVerifyAll(mockVC);
|
||||
}
|
||||
|
||||
- (void)testUpdatedViewportMetricsDoesNotResizeFlutterViewWhenNotAutoResizable {
|
||||
FlutterEngine* mockEngine = OCMPartialMock([[FlutterEngine alloc] init]);
|
||||
[mockEngine createShell:@"" libraryURI:@"" initialRoute:nil];
|
||||
|
||||
FlutterViewController* realVC = [[FlutterViewController alloc] initWithEngine:mockEngine
|
||||
nibName:nil
|
||||
bundle:nil];
|
||||
id mockVC = OCMPartialMock(realVC);
|
||||
mockEngine.viewController = mockVC;
|
||||
|
||||
OCMReject([mockVC updateAutoResizeConstraints]);
|
||||
|
||||
[mockVC setAutoResizable:NO];
|
||||
|
||||
[mockVC viewDidLayoutSubviews];
|
||||
|
||||
OCMVerifyAll(mockVC);
|
||||
}
|
||||
|
||||
- (void)testUpdateViewportMetricsIfNeeded_DoesNotInvokeEngineWhenShouldBeIgnoredDuringRotation {
|
||||
FlutterEngine* mockEngine = OCMPartialMock([[FlutterEngine alloc] init]);
|
||||
[mockEngine createShell:@"" libraryURI:@"" initialRoute:nil];
|
||||
@ -1319,36 +1279,6 @@ extern NSNotificationName const FlutterViewControllerWillDealloc;
|
||||
return mockTraitCollection;
|
||||
}
|
||||
|
||||
- (void)testTraitCollectionDidChangeCallsResetIntrinsicContentSizeWhenAutoResizable {
|
||||
// Setup test.
|
||||
id mockEngine = OCMPartialMock([[FlutterEngine alloc] init]);
|
||||
[mockEngine createShell:@"" libraryURI:@"" initialRoute:nil];
|
||||
|
||||
FlutterViewController* realVC = [[FlutterViewController alloc] initWithEngine:mockEngine
|
||||
nibName:nil
|
||||
bundle:nil];
|
||||
id partialMockVC = OCMPartialMock(realVC);
|
||||
|
||||
id mockFlutterView = OCMClassMock([FlutterView class]);
|
||||
OCMStub([partialMockVC flutterView]).andReturn(mockFlutterView);
|
||||
|
||||
// Ensure isAutoResizable is YES
|
||||
OCMStub([partialMockVC isAutoResizable]).andReturn(YES);
|
||||
|
||||
// Expect resetIntrinsicContentSize to be called on mockFlutterView
|
||||
OCMExpect([mockFlutterView resetIntrinsicContentSize]);
|
||||
|
||||
// Exercise behavior under test.
|
||||
[partialMockVC traitCollectionDidChange:nil];
|
||||
|
||||
// Verify behavior.
|
||||
OCMVerifyAll(mockFlutterView);
|
||||
|
||||
// Clean up mocks
|
||||
[partialMockVC stopMocking];
|
||||
[mockFlutterView stopMocking];
|
||||
}
|
||||
|
||||
#pragma mark - Platform Contrast
|
||||
|
||||
- (void)testItReportsNormalPlatformContrastByDefault {
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user