Skip to content
This repository was archived by the owner on Feb 25, 2025. It is now read-only.

Commit f6570a4

Browse files
committed
Fixed the ability to scroll to the top on ios 13 by introducing a
uiscrollview to FlutterViewController.
1 parent d2e2cc9 commit f6570a4

File tree

1 file changed

+56
-2
lines changed

1 file changed

+56
-2
lines changed

shell/platform/darwin/ios/framework/Source/FlutterViewController.mm

Lines changed: 56 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,8 @@
2323
#import "flutter/shell/platform/darwin/ios/framework/Source/platform_message_response_darwin.h"
2424
#import "flutter/shell/platform/darwin/ios/platform_view_ios.h"
2525

26+
constexpr int kMicrosecondsPerSecond = 1000 * 1000;
27+
2628
NSNotificationName const FlutterSemanticsUpdateNotification = @"FlutterSemanticsUpdate";
2729

2830
NSNotificationName const FlutterViewControllerWillDealloc = @"FlutterViewControllerWillDealloc";
@@ -86,7 +88,7 @@ - (void)invalidate {
8688
// This is left a FlutterBinaryMessenger privately for now to give people a chance to notice the
8789
// change. Unfortunately unless you have Werror turned on, incompatible pointers as arguments are
8890
// just a warning.
89-
@interface FlutterViewController () <FlutterBinaryMessenger>
91+
@interface FlutterViewController () <FlutterBinaryMessenger, UIScrollViewDelegate>
9092
@property(nonatomic, readwrite, getter=isDisplayingFlutterUI) BOOL displayingFlutterUI;
9193
@end
9294

@@ -329,6 +331,53 @@ - (void)loadView {
329331
self.view.autoresizingMask = UIViewAutoresizingFlexibleWidth | UIViewAutoresizingFlexibleHeight;
330332

331333
[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;
332381
}
333382

334383
#pragma mark - Managing launch views
@@ -569,7 +618,6 @@ - (void)flushOngoingTouches {
569618
flutter::PointerData pointer_data;
570619
pointer_data.Clear();
571620

572-
constexpr int kMicrosecondsPerSecond = 1000 * 1000;
573621
// Use current time.
574622
pointer_data.time_stamp = [[NSDate date] timeIntervalSince1970] * kMicrosecondsPerSecond;
575623

@@ -1152,6 +1200,12 @@ - (NSString*)contrastMode {
11521200
constexpr CGFloat kStandardStatusBarHeight = 20.0;
11531201

11541202
- (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+
}
11551209
CGFloat standardStatusBarHeight = kStandardStatusBarHeight;
11561210
if (@available(iOS 11, *)) {
11571211
standardStatusBarHeight = self.view.safeAreaInsets.top;

0 commit comments

Comments
 (0)