Skip to content
This repository was archived by the owner on Feb 25, 2025. It is now read-only.
Merged
Show file tree
Hide file tree
Changes from 5 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -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, setter=isViewOpaque:) BOOL isViewOpaque;
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Nit: @property(nonatomic, getter=isViewOpaque) BOOL viewOpaque; with the setter being inferred as setIsOpaque is more idiomatic. See -[UIView opaque] for another such use.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Done


@end

#endif // FLUTTER_FLUTTERVIEWCONTROLLER_H_
15 changes: 15 additions & 0 deletions shell/platform/darwin/ios/framework/Source/FlutterView.h
Original file line number Diff line number Diff line change
Expand Up @@ -9,10 +9,25 @@

#include <memory>

#include "flutter/fml/memory/weak_ptr.h"
#include "flutter/shell/common/shell.h"
#include "flutter/shell/platform/darwin/ios/ios_surface.h"

@protocol FlutterScreenshotDelegate <NSObject>
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Do you plan to use this elsewhere? If not, it's clear to me what advantage this provides over passing the FlutterViewController down directly like you did in the previous version.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

No, but it feels pretty wrong to pass a view controller directly into a view. I was looking at passing in just the shell, and after discussion with @chinmaygarde decided to go with a delegate - this way we avoid trying to share a pointer to the shell, and we avoid tying the ViewController implementation details to the View.


- (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<FlutterScreenshotDelegate>)delegate
opaque:(BOOL)opaque NS_DESIGNATED_INITIALIZER;
- (std::unique_ptr<shell::IOSSurface>)createSurface;

@end
Expand Down
52 changes: 25 additions & 27 deletions shell/platform/darwin/ios/framework/Source/FlutterView.mm
Original file line number Diff line number Diff line change
Expand Up @@ -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"
Expand All @@ -24,28 +22,36 @@ @interface FlutterView () <UIInputViewAudioFeedback>

@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<FlutterViewController*>(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<FlutterScreenshotDelegate> _delegate;

- (instancetype)init {
@throw([NSException exceptionWithName:@"FlutterView must initWithShell" reason:nil userInfo:nil]);
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

initWithDelegate (and elsewhere)

}

- (instancetype)initWithFrame:(CGRect)frame {
@throw([NSException exceptionWithName:@"FlutterView must initWithShell" reason:nil userInfo:nil]);
}

- (instancetype)initWithCoder:(NSCoder*)aDecoder {
@throw([NSException exceptionWithName:@"FlutterView must initWithShell" reason:nil userInfo:nil]);
}

- (instancetype)initWithDelegate:(id<FlutterScreenshotDelegate>)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<CAEAGLLayer*>(self.layer);
layer.allowsGroupOpacity = YES;
layer.opaque = YES;
CGFloat screenScale = [UIScreen mainScreen].scale;
layer.contentsScale = screenScale;
layer.rasterizationScale = screenScale;
Expand Down Expand Up @@ -84,16 +90,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;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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 () <FlutterTextInputDelegate>
@interface FlutterViewController () <FlutterTextInputDelegate, FlutterScreenshotDelegate>
@property(nonatomic, readonly) NSMutableDictionary* pluginPublications;
@end

Expand Down Expand Up @@ -58,6 +58,7 @@ @implementation FlutterViewController {
blink::ViewportMetrics _viewportMetrics;
int64_t _nextTextureId;
BOOL _initialized;
BOOL _isViewOpaque;

fml::scoped_nsobject<FlutterObservatoryPublisher> _publisher;
}
Expand All @@ -75,6 +76,8 @@ - (instancetype)initWithProject:(FlutterDartProject*)projectOrNil
else
_dartProject.reset([projectOrNil retain]);

self.isViewOpaque = YES;

[self performCommonViewControllerInitialization];
}

Expand Down Expand Up @@ -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.
Expand Down Expand Up @@ -180,6 +183,18 @@ - (BOOL)setupShell {
return true;
}

- (BOOL)isViewOpaque {
return _isViewOpaque;
}

- (void)isViewOpaque:(BOOL)value {
_isViewOpaque = 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"
Expand Down Expand Up @@ -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 {
Expand Down