diff --git a/shell/platform/darwin/ios/framework/Headers/FlutterViewController.h b/shell/platform/darwin/ios/framework/Headers/FlutterViewController.h index 5459c17f4f8e6..27b7330ba3457 100644 --- a/shell/platform/darwin/ios/framework/Headers/FlutterViewController.h +++ b/shell/platform/darwin/ios/framework/Headers/FlutterViewController.h @@ -93,6 +93,15 @@ FLUTTER_EXPORT */ @property(strong, nonatomic) UIView* splashScreenView; +/** + * Controls whether the created view will be opaque or not. + * + * Default is `YES`. Note that setting this to `NO` may negatively impact performance + * when using hardware acceleration, and toggling this will trigger a re-layout of the + * view. + */ +@property(nonatomic, getter=isViewOpaque) BOOL viewOpaque; + @end #endif // FLUTTER_FLUTTERVIEWCONTROLLER_H_ diff --git a/shell/platform/darwin/ios/framework/Source/FlutterView.h b/shell/platform/darwin/ios/framework/Source/FlutterView.h index 5e3d303401725..e9cd37281e63a 100644 --- a/shell/platform/darwin/ios/framework/Source/FlutterView.h +++ b/shell/platform/darwin/ios/framework/Source/FlutterView.h @@ -9,10 +9,25 @@ #include +#include "flutter/fml/memory/weak_ptr.h" +#include "flutter/shell/common/shell.h" #include "flutter/shell/platform/darwin/ios/ios_surface.h" +@protocol FlutterScreenshotDelegate + +- (shell::Rasterizer::Screenshot)takeScreenshot:(shell::Rasterizer::ScreenshotType)type + asBase64Encoded:(BOOL)base64Encode; + +@end + @interface FlutterView : UIView +- (instancetype)init NS_UNAVAILABLE; +- (instancetype)initWithFrame:(CGRect)frame NS_UNAVAILABLE; +- (instancetype)initWithCoder:(NSCoder*)aDecoder NS_UNAVAILABLE; + +- (instancetype)initWithDelegate:(id)delegate + opaque:(BOOL)opaque NS_DESIGNATED_INITIALIZER; - (std::unique_ptr)createSurface; @end diff --git a/shell/platform/darwin/ios/framework/Source/FlutterView.mm b/shell/platform/darwin/ios/framework/Source/FlutterView.mm index 32c54f632992f..63ee05acc832c 100644 --- a/shell/platform/darwin/ios/framework/Source/FlutterView.mm +++ b/shell/platform/darwin/ios/framework/Source/FlutterView.mm @@ -12,8 +12,6 @@ #include "flutter/fml/trace_event.h" #include "flutter/shell/common/platform_view.h" #include "flutter/shell/common/rasterizer.h" -#include "flutter/shell/common/shell.h" -#include "flutter/shell/platform/darwin/ios/framework/Source/FlutterViewController_Internal.h" #include "flutter/shell/platform/darwin/ios/ios_surface_gl.h" #include "flutter/shell/platform/darwin/ios/ios_surface_software.h" #include "third_party/skia/include/utils/mac/SkCGUtils.h" @@ -24,28 +22,42 @@ @interface FlutterView () @implementation FlutterView -- (FlutterViewController*)flutterViewController { - // Find the first view controller in the responder chain and see if it is a FlutterViewController. - for (UIResponder* responder = self.nextResponder; responder != nil; - responder = responder.nextResponder) { - if ([responder isKindOfClass:[UIViewController class]]) { - if ([responder isKindOfClass:[FlutterViewController class]]) { - return reinterpret_cast(responder); - } else { - // Should only happen if a non-FlutterViewController tries to somehow (via dynamic class - // resolution or reparenting) set a FlutterView as its view. - return nil; - } - } +id _delegate; + +- (instancetype)init { + @throw([NSException exceptionWithName:@"FlutterView must initWithDelegate" + reason:nil + userInfo:nil]); +} + +- (instancetype)initWithFrame:(CGRect)frame { + @throw([NSException exceptionWithName:@"FlutterView must initWithDelegate" + reason:nil + userInfo:nil]); +} + +- (instancetype)initWithCoder:(NSCoder*)aDecoder { + @throw([NSException exceptionWithName:@"FlutterView must initWithDelegate" + reason:nil + userInfo:nil]); +} + +- (instancetype)initWithDelegate:(id)delegate opaque:(BOOL)opaque { + FML_DCHECK(delegate) << "Delegate must not be nil."; + self = [super initWithFrame:CGRectNull]; + + if (self) { + _delegate = delegate; + self.layer.opaque = opaque; } - return nil; + + return self; } - (void)layoutSubviews { if ([self.layer isKindOfClass:[CAEAGLLayer class]]) { CAEAGLLayer* layer = reinterpret_cast(self.layer); layer.allowsGroupOpacity = YES; - layer.opaque = YES; CGFloat screenScale = [UIScreen mainScreen].scale; layer.contentsScale = screenScale; layer.rasterizationScale = screenScale; @@ -84,16 +96,8 @@ - (void)drawLayer:(CALayer*)layer inContext:(CGContextRef)context { return; } - FlutterViewController* controller = [self flutterViewController]; - - if (controller == nil) { - return; - } - - auto& shell = [controller shell]; - - auto screenshot = shell.Screenshot(shell::Rasterizer::ScreenshotType::UncompressedImage, - false /* base64 encode */); + auto screenshot = [_delegate takeScreenshot:shell::Rasterizer::ScreenshotType::UncompressedImage + asBase64Encoded:NO]; if (!screenshot.data || screenshot.data->isEmpty() || screenshot.frame_size.isEmpty()) { return; diff --git a/shell/platform/darwin/ios/framework/Source/FlutterViewController.mm b/shell/platform/darwin/ios/framework/Source/FlutterViewController.mm index 9327e8fbbb5e6..eeab410c37a0a 100644 --- a/shell/platform/darwin/ios/framework/Source/FlutterViewController.mm +++ b/shell/platform/darwin/ios/framework/Source/FlutterViewController.mm @@ -22,7 +22,7 @@ #include "flutter/shell/platform/darwin/ios/framework/Source/platform_message_response_darwin.h" #include "flutter/shell/platform/darwin/ios/platform_view_ios.h" -@interface FlutterViewController () +@interface FlutterViewController () @property(nonatomic, readonly) NSMutableDictionary* pluginPublications; @end @@ -58,6 +58,7 @@ @implementation FlutterViewController { blink::ViewportMetrics _viewportMetrics; int64_t _nextTextureId; BOOL _initialized; + BOOL _viewOpaque; fml::scoped_nsobject _publisher; } @@ -75,6 +76,8 @@ - (instancetype)initWithProject:(FlutterDartProject*)projectOrNil else _dartProject.reset([projectOrNil retain]); + self.viewOpaque = YES; + [self performCommonViewControllerInitialization]; } @@ -146,7 +149,7 @@ - (BOOL)setupShell { _threadHost.io_thread->GetTaskRunner() // io ); - _flutterView.reset([[FlutterView alloc] init]); + _flutterView.reset([[FlutterView alloc] initWithDelegate:self opaque:self.isViewOpaque]); // Lambda captures by pointers to ObjC objects are fine here because the create call is // synchronous. @@ -180,6 +183,18 @@ - (BOOL)setupShell { return true; } +- (BOOL)isViewOpaque { + return _viewOpaque; +} + +- (void)viewOpaque:(BOOL)value { + _viewOpaque = value; + if (_flutterView.get().layer.opaque != value) { + _flutterView.get().layer.opaque = value; + [_flutterView.get().layer setNeedsLayout]; + } +} + - (void)setupChannels { _localizationChannel.reset([[FlutterMethodChannel alloc] initWithName:@"flutter/localization" @@ -840,6 +855,14 @@ - (void)performAction:(FlutterTextInputAction)action withClient:(int)client { arguments:@[ @(client), actionString ]]; } +#pragma mark - Screenshot Delegate + +- (shell::Rasterizer::Screenshot)takeScreenshot:(shell::Rasterizer::ScreenshotType)type + asBase64Encoded:(BOOL)base64Encode { + FML_DCHECK(_shell) << "Cannot takeScreenshot without a shell"; + return _shell->Screenshot(type, base64Encode); +} + #pragma mark - Orientation updates - (void)onOrientationPreferencesUpdated:(NSNotification*)notification {