Skip to content

Commit 86dacc0

Browse files
authored
Revert "Support scrolling in iOS accessibility (flutter#26671)" (flutter#26803)
This reverts commit e56dd3a.
1 parent 6e9444b commit 86dacc0

File tree

5 files changed

+28
-352
lines changed

5 files changed

+28
-352
lines changed

lib/ui/semantics/semantics_node.h

Lines changed: 3 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -44,17 +44,12 @@ enum class SemanticsAction : int32_t {
4444
kSetText = 1 << 21,
4545
};
4646

47-
const int kVerticalScrollSemanticsActions =
47+
const int kScrollableSemanticsActions =
48+
static_cast<int32_t>(SemanticsAction::kScrollLeft) |
49+
static_cast<int32_t>(SemanticsAction::kScrollRight) |
4850
static_cast<int32_t>(SemanticsAction::kScrollUp) |
4951
static_cast<int32_t>(SemanticsAction::kScrollDown);
5052

51-
const int kHorizontalScrollSemanticsActions =
52-
static_cast<int32_t>(SemanticsAction::kScrollLeft) |
53-
static_cast<int32_t>(SemanticsAction::kScrollRight);
54-
55-
const int kScrollableSemanticsActions =
56-
kVerticalScrollSemanticsActions | kHorizontalScrollSemanticsActions;
57-
5853
/// C/C++ representation of `SemanticsFlags` defined in
5954
/// `lib/ui/semantics.dart`.
6055
///\warning This must match the `SemanticsFlags` enum in

shell/platform/darwin/ios/framework/Source/SemanticsObject.h

Lines changed: 1 addition & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -31,7 +31,7 @@ constexpr int32_t kRootNodeId = 0;
3131
* The parent of this node in the node tree. Will be nil for the root node and
3232
* during transient state changes.
3333
*/
34-
@property(nonatomic, assign) SemanticsObject* parent;
34+
@property(nonatomic, readonly) SemanticsObject* parent;
3535

3636
/**
3737
* The accessibility bridge that this semantics object is attached to. This
@@ -94,14 +94,6 @@ constexpr int32_t kRootNodeId = 0;
9494

9595
- (BOOL)onCustomAccessibilityAction:(FlutterCustomAccessibilityAction*)action;
9696

97-
/**
98-
* Called after accessibility bridge finishes a semantics update.
99-
*
100-
* Subclasses can override this method if they contain states that can only be
101-
* updated once every node in the accessibility tree has finished updating.
102-
*/
103-
- (void)accessibilityBridgeDidFinishUpdate;
104-
10597
#pragma mark - Designated initializers
10698

10799
- (instancetype)init __attribute__((unavailable("Use initWithBridge instead")));
@@ -167,18 +159,6 @@ constexpr int32_t kRootNodeId = 0;
167159

168160
@end
169161

170-
/// The semantics object for scrollable. This class creates an UIScrollView to interact with the
171-
/// iOS.
172-
@interface FlutterScrollableSemanticsObject : UIScrollView
173-
174-
- (instancetype)init NS_UNAVAILABLE;
175-
- (instancetype)initWithFrame:(CGRect)frame NS_UNAVAILABLE;
176-
- (instancetype)initWithCoder:(NSCoder*)coder NS_UNAVAILABLE;
177-
- (instancetype)initWithSemanticsObject:(SemanticsObject*)semanticsObject NS_DESIGNATED_INITIALIZER;
178-
- (void)accessibilityBridgeDidFinishUpdate;
179-
180-
@end
181-
182162
/**
183163
* Represents a semantics object that has children and hence has to be presented to the OS as a
184164
* UIAccessibilityContainer.

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

Lines changed: 21 additions & 204 deletions
Original file line numberDiff line numberDiff line change
@@ -34,58 +34,6 @@
3434
return flutter::SemanticsAction::kScrollUp;
3535
}
3636

37-
SkM44 GetGlobalTransform(SemanticsObject* reference) {
38-
SkM44 globalTransform = [reference node].transform;
39-
for (SemanticsObject* parent = [reference parent]; parent; parent = parent.parent) {
40-
globalTransform = parent.node.transform * globalTransform;
41-
}
42-
return globalTransform;
43-
}
44-
45-
SkPoint ApplyTransform(SkPoint& point, const SkM44& transform) {
46-
SkV4 vector = transform.map(point.x(), point.y(), 0, 1);
47-
return SkPoint::Make(vector.x / vector.w, vector.y / vector.w);
48-
}
49-
50-
CGPoint ConvertPointToGlobal(SemanticsObject* reference, CGPoint local_point) {
51-
SkM44 globalTransform = GetGlobalTransform(reference);
52-
SkPoint point = SkPoint::Make(local_point.x, local_point.y);
53-
point = ApplyTransform(point, globalTransform);
54-
// `rect` is in the physical pixel coordinate system. iOS expects the accessibility frame in
55-
// the logical pixel coordinate system. Therefore, we divide by the `scale` (pixel ratio) to
56-
// convert.
57-
CGFloat scale = [[[reference bridge]->view() window] screen].scale;
58-
auto result = CGPointMake(point.x() / scale, point.y() / scale);
59-
return [[reference bridge]->view() convertPoint:result toView:nil];
60-
}
61-
62-
CGRect ConvertRectToGlobal(SemanticsObject* reference, CGRect local_rect) {
63-
SkM44 globalTransform = GetGlobalTransform(reference);
64-
65-
SkPoint quad[4] = {
66-
SkPoint::Make(local_rect.origin.x, local_rect.origin.y), // top left
67-
SkPoint::Make(local_rect.origin.x + local_rect.size.width, local_rect.origin.y), // top right
68-
SkPoint::Make(local_rect.origin.x + local_rect.size.width,
69-
local_rect.origin.y + local_rect.size.height), // bottom right
70-
SkPoint::Make(local_rect.origin.x,
71-
local_rect.origin.y + local_rect.size.height) // bottom left
72-
};
73-
for (auto& point : quad) {
74-
point = ApplyTransform(point, globalTransform);
75-
}
76-
SkRect rect;
77-
NSCAssert(rect.setBoundsCheck(quad, 4), @"Transformed points can't form a rect");
78-
rect.setBounds(quad, 4);
79-
80-
// `rect` is in the physical pixel coordinate system. iOS expects the accessibility frame in
81-
// the logical pixel coordinate system. Therefore, we divide by the `scale` (pixel ratio) to
82-
// convert.
83-
CGFloat scale = [[[reference bridge]->view() window] screen].scale;
84-
auto result =
85-
CGRectMake(rect.x() / scale, rect.y() / scale, rect.width() / scale, rect.height() / scale);
86-
return UIAccessibilityConvertFrameToScreenCoordinates(result, [reference bridge]->view());
87-
}
88-
8937
} // namespace
9038

9139
@implementation FlutterSwitchSemanticsObject {
@@ -140,152 +88,6 @@ - (UIAccessibilityTraits)accessibilityTraits {
14088

14189
@end // FlutterSwitchSemanticsObject
14290

143-
@interface FlutterScrollableSemanticsObject ()
144-
@property(nonatomic, strong) SemanticsObject* semanticsObject;
145-
@end
146-
147-
@implementation FlutterScrollableSemanticsObject {
148-
fml::scoped_nsobject<SemanticsObjectContainer> _container;
149-
}
150-
151-
- (instancetype)initWithSemanticsObject:(SemanticsObject*)semanticsObject {
152-
self = [super initWithFrame:CGRectZero];
153-
if (self) {
154-
_semanticsObject = [semanticsObject retain];
155-
[semanticsObject.bridge->view() addSubview:self];
156-
}
157-
return self;
158-
}
159-
160-
- (void)dealloc {
161-
_container.get().semanticsObject = nil;
162-
[_semanticsObject release];
163-
[self removeFromSuperview];
164-
[super dealloc];
165-
}
166-
167-
- (UIView*)hitTest:(CGPoint)point withEvent:(UIEvent*)event {
168-
return nil;
169-
}
170-
171-
- (NSMethodSignature*)methodSignatureForSelector:(SEL)sel {
172-
NSMethodSignature* result = [super methodSignatureForSelector:sel];
173-
if (!result) {
174-
result = [_semanticsObject methodSignatureForSelector:sel];
175-
}
176-
return result;
177-
}
178-
179-
- (void)forwardInvocation:(NSInvocation*)anInvocation {
180-
[anInvocation setTarget:_semanticsObject];
181-
[anInvocation invoke];
182-
}
183-
184-
- (void)accessibilityBridgeDidFinishUpdate {
185-
// In order to make iOS think this UIScrollView is scrollable, the following
186-
// requirements must be true.
187-
// 1. contentSize must be bigger than the frame size.
188-
// 2. The scrollable isAccessibilityElement must return YES
189-
//
190-
// Once the requirements are met, the iOS uses contentOffset to determine
191-
// what scroll actions are available. e.g. If the view scrolls vertically and
192-
// contentOffset is 0.0, only the scroll down action is available.
193-
[self setFrame:[_semanticsObject accessibilityFrame]];
194-
[self setContentSize:[self contentSizeInternal]];
195-
[self setContentOffset:[self contentOffsetInternal] animated:NO];
196-
if (self.contentSize.width > self.frame.size.width ||
197-
self.contentSize.height > self.frame.size.height) {
198-
self.isAccessibilityElement = YES;
199-
} else {
200-
self.isAccessibilityElement = NO;
201-
;
202-
}
203-
}
204-
205-
- (void)setChildren:(NSArray<SemanticsObject*>*)children {
206-
[_semanticsObject setChildren:children];
207-
// The children's parent is pointing to _semanticsObject, need to manually
208-
// set it this object.
209-
for (SemanticsObject* child in _semanticsObject.children) {
210-
child.parent = (SemanticsObject*)self;
211-
}
212-
}
213-
214-
- (id)accessibilityContainer {
215-
if (_container == nil) {
216-
_container.reset([[SemanticsObjectContainer alloc]
217-
initWithSemanticsObject:(SemanticsObject*)self
218-
bridge:[_semanticsObject bridge]]);
219-
}
220-
return _container.get();
221-
}
222-
223-
// private methods
224-
225-
- (CGSize)contentSizeInternal {
226-
CGRect result;
227-
const SkRect& rect = _semanticsObject.node.rect;
228-
if (_semanticsObject.node.actions & flutter::kVerticalScrollSemanticsActions) {
229-
result = CGRectMake(rect.x(), rect.y(), rect.width(),
230-
rect.height() + _semanticsObject.node.scrollExtentMax);
231-
} else if (_semanticsObject.node.actions & flutter::kHorizontalScrollSemanticsActions) {
232-
result = CGRectMake(rect.x(), rect.y(), rect.width() + _semanticsObject.node.scrollExtentMax,
233-
rect.height());
234-
} else {
235-
result = CGRectMake(rect.x(), rect.y(), rect.width(), rect.height());
236-
}
237-
return ConvertRectToGlobal(_semanticsObject, result).size;
238-
}
239-
240-
- (CGPoint)contentOffsetInternal {
241-
CGPoint result;
242-
CGPoint origin = self.frame.origin;
243-
const SkRect& rect = _semanticsObject.node.rect;
244-
if (_semanticsObject.node.actions & flutter::kVerticalScrollSemanticsActions) {
245-
result = ConvertPointToGlobal(
246-
_semanticsObject, CGPointMake(rect.x(), rect.y() + _semanticsObject.node.scrollPosition));
247-
} else if (_semanticsObject.node.actions & flutter::kHorizontalScrollSemanticsActions) {
248-
result = ConvertPointToGlobal(
249-
_semanticsObject, CGPointMake(rect.x() + _semanticsObject.node.scrollPosition, rect.y()));
250-
} else {
251-
result = origin;
252-
}
253-
return CGPointMake(result.x - origin.x, result.y - origin.y);
254-
}
255-
256-
// The following methods are explicitly forwarded to the wrapped SemanticsObject because the
257-
// forwarding logic above doesn't apply to them since they are also implemented in the
258-
// UIScrollView class, the base class.
259-
260-
- (BOOL)accessibilityActivate {
261-
return [_semanticsObject accessibilityActivate];
262-
}
263-
264-
- (void)accessibilityIncrement {
265-
[_semanticsObject accessibilityIncrement];
266-
}
267-
268-
- (void)accessibilityDecrement {
269-
[_semanticsObject accessibilityDecrement];
270-
}
271-
272-
- (BOOL)accessibilityScroll:(UIAccessibilityScrollDirection)direction {
273-
return [_semanticsObject accessibilityScroll:direction];
274-
}
275-
276-
- (BOOL)accessibilityPerformEscape {
277-
return [_semanticsObject accessibilityPerformEscape];
278-
}
279-
280-
- (void)accessibilityElementDidBecomeFocused {
281-
[_semanticsObject accessibilityElementDidBecomeFocused];
282-
}
283-
284-
- (void)accessibilityElementDidLoseFocus {
285-
[_semanticsObject accessibilityElementDidLoseFocus];
286-
}
287-
@end // FlutterScrollableSemanticsObject
288-
28991
@implementation FlutterCustomAccessibilityAction {
29092
}
29193
@end
@@ -372,9 +174,6 @@ - (void)setSemanticsNode:(const flutter::SemanticsNode*)node {
372174
_node = *node;
373175
}
374176

375-
- (void)accessibilityBridgeDidFinishUpdate { /* Do nothing by default */
376-
}
377-
378177
/**
379178
* Whether calling `setSemanticsNode:` with `node` would cause a layout change.
380179
*/
@@ -599,9 +398,27 @@ - (CGRect)accessibilityFrame {
599398
}
600399

601400
- (CGRect)globalRect {
602-
const SkRect& rect = [self node].rect;
603-
CGRect localRect = CGRectMake(rect.x(), rect.y(), rect.width(), rect.height());
604-
return ConvertRectToGlobal(self, localRect);
401+
SkM44 globalTransform = [self node].transform;
402+
for (SemanticsObject* parent = [self parent]; parent; parent = parent.parent) {
403+
globalTransform = parent.node.transform * globalTransform;
404+
}
405+
406+
SkPoint quad[4];
407+
[self node].rect.toQuad(quad);
408+
for (auto& point : quad) {
409+
SkV4 vector = globalTransform.map(point.x(), point.y(), 0, 1);
410+
point.set(vector.x / vector.w, vector.y / vector.w);
411+
}
412+
SkRect rect;
413+
rect.setBounds(quad, 4);
414+
415+
// `rect` is in the physical pixel coordinate system. iOS expects the accessibility frame in
416+
// the logical pixel coordinate system. Therefore, we divide by the `scale` (pixel ratio) to
417+
// convert.
418+
CGFloat scale = [[[self bridge]->view() window] screen].scale;
419+
auto result =
420+
CGRectMake(rect.x() / scale, rect.y() / scale, rect.width() / scale, rect.height() / scale);
421+
return UIAccessibilityConvertFrameToScreenCoordinates(result, [self bridge]->view());
605422
}
606423

607424
#pragma mark - UIAccessibilityElement protocol

0 commit comments

Comments
 (0)