|
23 | 23 | #import "flutter/shell/platform/darwin/ios/framework/Source/platform_message_response_darwin.h"
|
24 | 24 | #import "flutter/shell/platform/darwin/ios/platform_view_ios.h"
|
25 | 25 |
|
| 26 | +constexpr int kMicrosecondsPerSecond = 1000 * 1000; |
| 27 | + |
26 | 28 | NSNotificationName const FlutterSemanticsUpdateNotification = @"FlutterSemanticsUpdate";
|
27 | 29 |
|
28 | 30 | NSNotificationName const FlutterViewControllerWillDealloc = @"FlutterViewControllerWillDealloc";
|
@@ -86,7 +88,7 @@ - (void)invalidate {
|
86 | 88 | // This is left a FlutterBinaryMessenger privately for now to give people a chance to notice the
|
87 | 89 | // change. Unfortunately unless you have Werror turned on, incompatible pointers as arguments are
|
88 | 90 | // just a warning.
|
89 |
| -@interface FlutterViewController () <FlutterBinaryMessenger> |
| 91 | +@interface FlutterViewController () <FlutterBinaryMessenger, UIScrollViewDelegate> |
90 | 92 | @property(nonatomic, readwrite, getter=isDisplayingFlutterUI) BOOL displayingFlutterUI;
|
91 | 93 | @end
|
92 | 94 |
|
@@ -329,6 +331,53 @@ - (void)loadView {
|
329 | 331 | self.view.autoresizingMask = UIViewAutoresizingFlexibleWidth | UIViewAutoresizingFlexibleHeight;
|
330 | 332 |
|
331 | 333 | [self installSplashScreenViewIfNecessary];
|
| 334 | + if (@available(iOS 13, *)) { |
| 335 | + // This is a workaround on iOS 13 and higher. There isn't a way to get touches on the status |
| 336 | + // bar to trigger scrolling to the top of a scroll view. We place a UIScrollView underneath |
| 337 | + // the status bar with a content offset so we can get those events. |
| 338 | + // See also: https://github.com/flutter/flutter/issues/35050 |
| 339 | + UIScrollView* scrollView = [[UIScrollView alloc] init]; |
| 340 | + scrollView.autoresizingMask = UIViewAutoresizingFlexibleWidth; |
| 341 | + // The color shouldn't matter since it is underneath the status bar. |
| 342 | + scrollView.backgroundColor = [UIColor colorWithRed:255 green:255 blue:255 alpha:255]; |
| 343 | + // Alpha can't be completely zero, otherwise we won't get events. The scroll view should be |
| 344 | + // obscured by the status bar so this shouldn't matter. |
| 345 | + scrollView.alpha = 0.1f; |
| 346 | + scrollView.delegate = self; |
| 347 | + // This is an arbitrary small size. |
| 348 | + scrollView.contentSize = CGSizeMake(10, 10); |
| 349 | + scrollView.opaque = NO; |
| 350 | + // This is an arbitrary offset that is not CGPointZero. |
| 351 | + scrollView.contentOffset = CGPointMake(10, 10); |
| 352 | + [self.view addSubview:scrollView]; |
| 353 | + UIStatusBarManager* manager = |
| 354 | + [UIApplication sharedApplication].keyWindow.windowScene.statusBarManager; |
| 355 | + CGRect statusBarFrame = manager.statusBarFrame; |
| 356 | + scrollView.frame = CGRectMake(0, 0, statusBarFrame.size.width, statusBarFrame.size.height); |
| 357 | + [scrollView release]; |
| 358 | + } |
| 359 | +} |
| 360 | + |
| 361 | +static void sendFakeTouchEvent(FlutterEngine* engine, |
| 362 | + CGPoint location, |
| 363 | + flutter::PointerData::Change change) { |
| 364 | + const CGFloat scale = [UIScreen mainScreen].scale; |
| 365 | + flutter::PointerData pointer_data; |
| 366 | + pointer_data.Clear(); |
| 367 | + pointer_data.physical_x = location.x * scale; |
| 368 | + pointer_data.physical_y = location.y * scale; |
| 369 | + pointer_data.kind = flutter::PointerData::DeviceKind::kTouch; |
| 370 | + pointer_data.time_stamp = CFAbsoluteTimeGetCurrent() * kMicrosecondsPerSecond; |
| 371 | + auto packet = std::make_unique<flutter::PointerDataPacket>(/*count=*/1); |
| 372 | + pointer_data.change = change; |
| 373 | + packet->SetPointerData(0, pointer_data); |
| 374 | + [engine dispatchPointerDataPacket:std::move(packet)]; |
| 375 | +} |
| 376 | + |
| 377 | +- (BOOL)scrollViewShouldScrollToTop:(UIScrollView*)scrollView { |
| 378 | + sendFakeTouchEvent(_engine.get(), CGPointZero, flutter::PointerData::Change::kDown); |
| 379 | + sendFakeTouchEvent(_engine.get(), CGPointZero, flutter::PointerData::Change::kUp); |
| 380 | + return NO; |
332 | 381 | }
|
333 | 382 |
|
334 | 383 | #pragma mark - Managing launch views
|
@@ -569,7 +618,6 @@ - (void)flushOngoingTouches {
|
569 | 618 | flutter::PointerData pointer_data;
|
570 | 619 | pointer_data.Clear();
|
571 | 620 |
|
572 |
| - constexpr int kMicrosecondsPerSecond = 1000 * 1000; |
573 | 621 | // Use current time.
|
574 | 622 | pointer_data.time_stamp = [[NSDate date] timeIntervalSince1970] * kMicrosecondsPerSecond;
|
575 | 623 |
|
@@ -1152,6 +1200,12 @@ - (NSString*)contrastMode {
|
1152 | 1200 | constexpr CGFloat kStandardStatusBarHeight = 20.0;
|
1153 | 1201 |
|
1154 | 1202 | - (void)handleStatusBarTouches:(UIEvent*)event {
|
| 1203 | + if (@available(iOS 13, *)) { |
| 1204 | + // This call shouldn't be happening as of iOS 13, to be safe we ignore it, |
| 1205 | + // scrollViewShouldScrollToTop: will handle this functionality. |
| 1206 | + // See also: https://github.com/flutter/flutter/issues/35050 |
| 1207 | + return; |
| 1208 | + } |
1155 | 1209 | CGFloat standardStatusBarHeight = kStandardStatusBarHeight;
|
1156 | 1210 | if (@available(iOS 11, *)) {
|
1157 | 1211 | standardStatusBarHeight = self.view.safeAreaInsets.top;
|
|
0 commit comments