Support scroll to top on iOS statusbar touches (flutter/engine#3375)
On iOS, when a tap is detected in the status bar, provide a means to pass that touch event through to one or more FlutterViewControllers to trigger a scroll to top. In iOS apps, scroll to top should occur under the following conditions: 1. There is one and only one UIScrollView visible with scrollsToTop == YES. 2. The status-bar is in standard height mode, not in double-height mode. In double-height mode, the expected behaviour is to trigger a switch to the application associated with the double-height status bar. 3. A tap or a drag gesture occurs that is entirely constrained to the status bar frame. (We currently only handle the tap scenario). Unfortunately, AppDelegates only get touchesBegan events for status bar taps, though get get touchesBegan and touchesEnded events for drags within the status bar frame. As such, we currently synthesise the touchesEnded event for taps.
This commit is contained in:
@@ -37,6 +37,8 @@ FLUTTER_EXPORT
|
||||
- (void)removeAsyncMessageListener:
|
||||
(NSObject<FlutterAsyncMessageListener>*)listener;
|
||||
|
||||
- (void)handleStatusBarTouches:(UIEvent *)event;
|
||||
|
||||
@end
|
||||
|
||||
__BEGIN_DECLS
|
||||
|
||||
@@ -271,6 +271,11 @@ static inline PointerChangeMapperPhase PointerChangePhaseFromUITouchPhase(
|
||||
}
|
||||
|
||||
- (void)dispatchTouches:(NSSet*)touches phase:(UITouchPhase)phase {
|
||||
// Note: we cannot rely on touch.phase, since in some cases, e.g.,
|
||||
// handleStatusBarTouches, we synthesize touches from existing events.
|
||||
//
|
||||
// TODO(cbracken) consider creating out own class with the touch fields we
|
||||
// need.
|
||||
auto eventTypePhase = PointerChangePhaseFromUITouchPhase(phase);
|
||||
const CGFloat scale = [UIScreen mainScreen].scale;
|
||||
auto packet = std::make_unique<blink::PointerDataPacket>(touches.count);
|
||||
@@ -498,6 +503,34 @@ static inline PointerChangeMapperPhase PointerChangePhaseFromUITouchPhase(
|
||||
[super dealloc];
|
||||
}
|
||||
|
||||
#pragma mark - Status Bar touch event handling
|
||||
|
||||
// Standard iOS status bar height in pixels.
|
||||
constexpr CGFloat kStandardStatusBarHeight = 20.0;
|
||||
|
||||
- (void)handleStatusBarTouches:(UIEvent *)event {
|
||||
// If the status bar is double-height, don't handle status bar taps. iOS
|
||||
// should open the app associated with the status bar.
|
||||
CGRect statusBarFrame = [UIApplication sharedApplication].statusBarFrame;
|
||||
if (statusBarFrame.size.height != kStandardStatusBarHeight) {
|
||||
return;
|
||||
}
|
||||
|
||||
// If we detect a touch in the status bar, synthesize a fake touch begin/end.
|
||||
for (UITouch *touch in event.allTouches) {
|
||||
if (touch.phase == UITouchPhaseBegan && touch.tapCount > 0) {
|
||||
CGPoint windowLoc = [touch locationInView:nil];
|
||||
CGPoint screenLoc = [touch.window convertPoint:windowLoc toWindow:nil];
|
||||
if (CGRectContainsPoint(statusBarFrame, screenLoc)) {
|
||||
NSSet *statusbarTouches = [NSSet setWithObject:touch];
|
||||
[self dispatchTouches:statusbarTouches phase:UITouchPhaseBegan];
|
||||
[self dispatchTouches:statusbarTouches phase:UITouchPhaseEnded];
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#pragma mark - Status bar style
|
||||
|
||||
- (UIStatusBarStyle)preferredStatusBarStyle {
|
||||
|
||||
Reference in New Issue
Block a user