-
Notifications
You must be signed in to change notification settings - Fork 6k
[ios] view controller based status bar #42643
Changes from 1 commit
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -30,11 +30,21 @@ | |
| "io.flutter.plugin.platform.SystemChromeOverlayNotificationName"; | ||
| const char* const kOverlayStyleUpdateNotificationKey = | ||
| "io.flutter.plugin.platform.SystemChromeOverlayNotificationKey"; | ||
| const char* const kStatusBarHiddenUpdateNotificationName = | ||
| "io.flutter.plugin.platform.StatusBarHiddenNotificationName"; | ||
| const char* const kStatusBarHiddenUpdateNotificationKey = | ||
| "io.flutter.plugin.platform.StatusBarHiddenNotificationKey"; | ||
|
|
||
| } // namespace flutter | ||
|
|
||
| using namespace flutter; | ||
|
|
||
| @interface FlutterPlatformPlugin () | ||
|
|
||
| @property(nonatomic, assign) BOOL enableViewControllerBasedStatusBarAppearance; | ||
|
|
||
| @end | ||
|
|
||
| @implementation FlutterPlatformPlugin { | ||
| fml::WeakPtr<FlutterEngine> _engine; | ||
| // Used to detect whether this device has live text input ability or not. | ||
|
|
@@ -47,6 +57,9 @@ - (instancetype)initWithEngine:(fml::WeakPtr<FlutterEngine>)engine { | |
|
|
||
| if (self) { | ||
| _engine = engine; | ||
| NSNumber* infoValue = [[NSBundle mainBundle] | ||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. This code (the next line, specifically) will crash if someone puts the wrong key type in their Info.plist. Since that's a developer error that's probably okay, but it might be nice to check the type and log (in debug only) a clear error message if it's wrong.
Contributor
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. done |
||
| objectForInfoDictionaryKey:@"UIViewControllerBasedStatusBarAppearance"]; | ||
| _enableViewControllerBasedStatusBarAppearance = (infoValue == nil || [infoValue boolValue]); | ||
|
stuartmorgan-g marked this conversation as resolved.
Outdated
|
||
| } | ||
|
|
||
| return self; | ||
|
|
@@ -158,14 +171,7 @@ - (void)setSystemChromeApplicationSwitcherDescription:(NSDictionary*)object { | |
| } | ||
|
|
||
| - (void)setSystemChromeEnabledSystemUIOverlays:(NSArray*)overlays { | ||
| // Checks if the top status bar should be visible. This platform ignores all | ||
| // other overlays | ||
|
|
||
| // We opt out of view controller based status bar visibility since we want | ||
| // to be able to modify this on the fly. The key used is | ||
| // UIViewControllerBasedStatusBarAppearance | ||
| [UIApplication sharedApplication].statusBarHidden = | ||
| ![overlays containsObject:@"SystemUiOverlay.top"]; | ||
| BOOL statusBarShouldBeHidden = ![overlays containsObject:@"SystemUiOverlay.top"]; | ||
| if ([overlays containsObject:@"SystemUiOverlay.bottom"]) { | ||
| [[NSNotificationCenter defaultCenter] | ||
| postNotificationName:FlutterViewControllerShowHomeIndicator | ||
|
|
@@ -175,26 +181,46 @@ - (void)setSystemChromeEnabledSystemUIOverlays:(NSArray*)overlays { | |
| postNotificationName:FlutterViewControllerHideHomeIndicator | ||
| object:nil]; | ||
| } | ||
| if (self.enableViewControllerBasedStatusBarAppearance) { | ||
| // This notification is respected by the iOS embedder | ||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Nit: comments are missing periods.
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Why are we communicating within the embedder via notifications, rather than an explicit delegate or callback relationship? My previous experience with notification for communication within a project is that it's significant headache for understanding how components interact.
Contributor
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I just copy pasted the old code and didn't really dig into the reason behind it. Most of the notification code are 5+ year old. And looking at the https://github.com/flutter/engine/blob/8c3eeb572f91b86c4fc6a5b90e85d6d008de3dc7/shell/platform/darwin/ios/BUILD.gn at the time, both FlutterPlatformPlugin and FlutterViewController are under the same build target. So I think it was probably just overlooked. I will remove this notification and also create an issue to move away from the notifications. |
||
| [[NSNotificationCenter defaultCenter] | ||
| postNotificationName:@(kStatusBarHiddenUpdateNotificationName) | ||
| object:nil | ||
| userInfo:@{ | ||
| @(kStatusBarHiddenUpdateNotificationKey) : @(statusBarShouldBeHidden) | ||
| }]; | ||
| } else { | ||
| // Checks if the top status bar should be visible. This platform ignores all | ||
| // other overlays | ||
|
|
||
| // We opt out of view controller based status bar visibility since we want | ||
| // to be able to modify this on the fly. The key used is | ||
| // UIViewControllerBasedStatusBarAppearance | ||
| [UIApplication sharedApplication].statusBarHidden = statusBarShouldBeHidden; | ||
| } | ||
| } | ||
|
|
||
| - (void)setSystemChromeEnabledSystemUIMode:(NSString*)mode { | ||
| // Checks if the top status bar should be visible, reflected by edge to edge setting. This | ||
| // platform ignores all other system ui modes. | ||
|
|
||
| // We opt out of view controller based status bar visibility since we want | ||
| // to be able to modify this on the fly. The key used is | ||
| // UIViewControllerBasedStatusBarAppearance | ||
| [UIApplication sharedApplication].statusBarHidden = | ||
| ![mode isEqualToString:@"SystemUiMode.edgeToEdge"]; | ||
| if ([mode isEqualToString:@"SystemUiMode.edgeToEdge"]) { | ||
| BOOL edgeToEdge = [mode isEqualToString:@"SystemUiMode.edgeToEdge"]; | ||
| if (self.enableViewControllerBasedStatusBarAppearance) { | ||
| // This notification is respected by the iOS embedder | ||
| [[NSNotificationCenter defaultCenter] | ||
| postNotificationName:FlutterViewControllerShowHomeIndicator | ||
| object:nil]; | ||
| postNotificationName:@(kStatusBarHiddenUpdateNotificationName) | ||
| object:nil | ||
| userInfo:@{@(kStatusBarHiddenUpdateNotificationKey) : @(!edgeToEdge)}]; | ||
| } else { | ||
| [[NSNotificationCenter defaultCenter] | ||
| postNotificationName:FlutterViewControllerHideHomeIndicator | ||
| object:nil]; | ||
| // Checks if the top status bar should be visible, reflected by edge to edge setting. This | ||
| // platform ignores all other system ui modes. | ||
|
|
||
| // We opt out of view controller based status bar visibility since we want | ||
| // to be able to modify this on the fly. The key used is | ||
| // UIViewControllerBasedStatusBarAppearance | ||
| [UIApplication sharedApplication].statusBarHidden = !edgeToEdge; | ||
| } | ||
| [[NSNotificationCenter defaultCenter] | ||
| postNotificationName:edgeToEdge ? FlutterViewControllerShowHomeIndicator | ||
| : FlutterViewControllerHideHomeIndicator | ||
| object:nil]; | ||
| } | ||
|
|
||
| - (void)restoreSystemChromeSystemUIOverlays { | ||
|
|
@@ -220,11 +246,7 @@ - (void)setSystemChromeSystemUIOverlayStyle:(NSDictionary*)message { | |
| return; | ||
| } | ||
|
|
||
| NSNumber* infoValue = [[NSBundle mainBundle] | ||
| objectForInfoDictionaryKey:@"UIViewControllerBasedStatusBarAppearance"]; | ||
| Boolean delegateToViewController = (infoValue == nil || [infoValue boolValue]); | ||
|
|
||
| if (delegateToViewController) { | ||
| if (self.enableViewControllerBasedStatusBarAppearance) { | ||
| // This notification is respected by the iOS embedder | ||
| [[NSNotificationCenter defaultCenter] | ||
| postNotificationName:@(kOverlayStyleUpdateNotificationName) | ||
|
|
||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -103,6 +103,13 @@ @interface FlutterViewController () <FlutterBinaryMessenger, UIScrollViewDelegat | |
| @property(nonatomic, retain) | ||
| UIRotationGestureRecognizer* rotationGestureRecognizer API_AVAILABLE(ios(13.4)); | ||
|
|
||
| // Whether the status bar is prefered hidden. | ||
| // | ||
| // The |UIViewController:prefersStatusBarHidden| of this ViewController is overriden and returns | ||
| // `flutterPreferesStatusBarHidden`. Only works when `UIViewControllerBasedStatusBarAppearance` in | ||
| // info.plist of the app project is `true`. | ||
| @property(nonatomic, assign) BOOL flutterPreferesStatusBarHidden; | ||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Typo: prefers |
||
|
|
||
| /** | ||
| * Creates and registers plugins used by this view controller. | ||
| */ | ||
|
|
@@ -299,6 +306,10 @@ - (void)setupNotificationCenterObservers { | |
| selector:@selector(onPreferredStatusBarStyleUpdated:) | ||
| name:@(flutter::kOverlayStyleUpdateNotificationName) | ||
| object:nil]; | ||
| [center addObserver:self | ||
| selector:@selector(onPreferredStatusBarHiddenUpdated:) | ||
| name:@(flutter::kStatusBarHiddenUpdateNotificationName) | ||
| object:nil]; | ||
|
|
||
| [center addObserver:self | ||
| selector:@selector(applicationBecameActive:) | ||
|
|
@@ -2081,6 +2092,31 @@ - (void)onPreferredStatusBarStyleUpdated:(NSNotification*)notification { | |
| }); | ||
| } | ||
|
|
||
| - (void)onPreferredStatusBarHiddenUpdated:(NSNotification*)notification { | ||
| FML_DCHECK( | ||
| [notification.name isEqualToString:@(flutter::kStatusBarHiddenUpdateNotificationName)]); | ||
| // Notifications may not be on the iOS UI thread | ||
| fml::TaskRunner::RunNowOrPostTask([_engine.get() platformTaskRunner], [&]() { | ||
| NSDictionary* info = notification.userInfo; | ||
|
|
||
| NSNumber* update = info[@(flutter::kStatusBarHiddenUpdateNotificationKey)]; | ||
|
|
||
| if (update == nil) { | ||
| return; | ||
| } | ||
|
|
||
| BOOL hidden = [update boolValue]; | ||
| if (hidden != self.flutterPreferesStatusBarHidden) { | ||
| self.flutterPreferesStatusBarHidden = hidden; | ||
| [self setNeedsStatusBarAppearanceUpdate]; | ||
| } | ||
| }); | ||
| } | ||
|
|
||
| - (BOOL)prefersStatusBarHidden { | ||
| return self.flutterPreferesStatusBarHidden; | ||
| } | ||
|
|
||
| #pragma mark - Platform views | ||
|
|
||
| - (std::shared_ptr<flutter::FlutterPlatformViewsController>&)platformViewsController { | ||
|
|
||
Uh oh!
There was an error while loading. Please reload this page.