Skip to content

Commit e92379f

Browse files
committed
fix: Apply zoom and content offset simultaneously on iOS
1 parent d149662 commit e92379f

File tree

5 files changed

+114
-18
lines changed

5 files changed

+114
-18
lines changed

src/Uno.UI/UI/Xaml/Controls/ListViewBase/ListViewBaseScrollContentPresenter.iOS.cs

Lines changed: 39 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -2,11 +2,10 @@
22
using System.Collections.Generic;
33
using System.Text;
44
using CoreGraphics;
5+
using UIKit;
56
using Uno.Extensions;
6-
77
using Uno.Foundation.Logging;
88
using Windows.Foundation;
9-
using UIKit;
109

1110
#if NET6_0_OR_GREATER
1211
using ObjCRuntime;
@@ -26,6 +25,44 @@ public UIEdgeInsets ContentInset
2625

2726
Size? IScrollContentPresenter.CustomContentExtent => NativePanel?.ContentSize;
2827

28+
CGPoint IUIScrollView.ContentOffset => NativePanel?.ContentOffset ?? default(CGPoint);
29+
30+
nfloat IUIScrollView.ZoomScale => NativePanel?.ZoomScale ?? default(nfloat);
31+
32+
void IUIScrollView.ApplyZoomScale(nfloat scale, bool animated)
33+
{
34+
if (NativePanel == null)
35+
{
36+
return;
37+
}
38+
39+
if (animated)
40+
{
41+
NativePanel.SetZoomScale(scale, animated);
42+
}
43+
else
44+
{
45+
NativePanel.ZoomScale = scale;
46+
}
47+
}
48+
49+
void IUIScrollView.ApplyContentOffset(CGPoint contentOffset, bool animated)
50+
{
51+
if (NativePanel == null)
52+
{
53+
return;
54+
}
55+
56+
if (animated)
57+
{
58+
NativePanel.SetContentOffset(contentOffset, animated);
59+
}
60+
else
61+
{
62+
NativePanel.ContentOffset = contentOffset;
63+
}
64+
}
65+
2966
public void SetContentOffset(CGPoint contentOffset, bool animated)
3067
{
3168
NativePanel?.SetContentOffset(contentOffset, animated);

src/Uno.UI/UI/Xaml/Controls/ScrollContentPresenter/IUIScrollView.iOSmacOS.cs

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -13,10 +13,14 @@ internal interface IUIScrollView
1313
{
1414
CGPoint UpperScrollLimit { get; }
1515

16-
void SetContentOffset(CGPoint contentOffset, bool animated);
16+
CGPoint ContentOffset { get; }
17+
18+
void ApplyContentOffset(CGPoint contentOffset, bool animated);
1719

1820
#if __IOS__
19-
void SetZoomScale(nfloat scale, bool animated);
21+
nfloat ZoomScale { get; }
22+
23+
void ApplyZoomScale(nfloat scale, bool animated);
2024
#endif
2125
}
2226
}

src/Uno.UI/UI/Xaml/Controls/ScrollContentPresenter/NativeScrollContentPresenter.iOS.cs

Lines changed: 31 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -43,6 +43,11 @@ partial class NativeScrollContentPresenter : UIScrollView, DependencyObject, ISe
4343
private bool _isInAnimatedScroll;
4444

4545
CGPoint IUIScrollView.UpperScrollLimit => UpperScrollLimit;
46+
47+
CGPoint IUIScrollView.ContentOffset => ContentOffset;
48+
49+
nfloat IUIScrollView.ZoomScale => ZoomScale;
50+
4651
internal CGPoint UpperScrollLimit
4752
{
4853
get
@@ -158,6 +163,30 @@ public override void SetContentOffset(CGPoint contentOffset, bool animated)
158163
}
159164
}
160165

166+
void IUIScrollView.ApplyZoomScale(nfloat scale, bool animated)
167+
{
168+
if (!animated)
169+
{
170+
ZoomScale = scale;
171+
}
172+
else
173+
{
174+
base.SetZoomScale(scale, true);
175+
}
176+
}
177+
178+
void IUIScrollView.ApplyContentOffset(CGPoint contentOffset, bool animated)
179+
{
180+
if (!animated)
181+
{
182+
ContentOffset = contentOffset;
183+
}
184+
else
185+
{
186+
base.SetContentOffset(contentOffset, true);
187+
}
188+
}
189+
161190
partial void OnContentChanged(UIView previousView, UIView newView)
162191
{
163192
// If Content is a view it may have already been set as Content somewhere else in certain scenarios
@@ -166,7 +195,7 @@ partial void OnContentChanged(UIView previousView, UIView newView)
166195
previousView.RemoveFromSuperview();
167196
}
168197

169-
// Ensure we're working with an empty view, in case previously removed views were missed.
198+
// Ensure we're working with an empty view, in case previously removed views were missed.
170199
while (Subviews.Length > 0)
171200
{
172201
Subviews[0].RemoveFromSuperview();
@@ -534,7 +563,7 @@ public override void TouchesBegan(NSSet touches, UIEvent evt)
534563
// (e.g. it would prevent all sub-sequent events for the given pointer).
535564

536565
_touchTarget = parent;
537-
_touchTarget.TouchesBegan(touches, evt, canBubbleNatively: true);
566+
_touchTarget.TouchesBegan(touches, evt, canBubbleNatively: true);
538567
}
539568
}
540569

src/Uno.UI/UI/Xaml/Controls/ScrollViewer/ScrollViewer.iOS.cs

Lines changed: 14 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -86,24 +86,31 @@ private bool ChangeViewNative(double? horizontalOffset, double? verticalOffset,
8686
var desiredOffsets = new Windows.Foundation.Point(horizontalOffset ?? HorizontalOffset, verticalOffset ?? VerticalOffset);
8787
var clampedOffsets = new Windows.Foundation.Point(MathEx.Clamp(desiredOffsets.X, 0, limit.X), MathEx.Clamp(desiredOffsets.Y, 0, limit.Y));
8888

89+
var contentOffsetChange =
90+
(horizontalOffset != null && _scrollableContainer.ContentOffset.X != horizontalOffset) ||
91+
(verticalOffset != null && _scrollableContainer.ContentOffset.Y != verticalOffset);
92+
93+
var zoomFactorChange = zoomFactor != null && _scrollableContainer.ZoomScale != zoomFactor;
94+
8995
var success = desiredOffsets == clampedOffsets;
96+
97+
if (zoomFactor is { } zoom && zoom != _scrollableContainer.ZoomScale)
98+
{
99+
_scrollableContainer.ApplyZoomScale(zoom, !disableAnimation);
100+
}
101+
90102
if (!success && IsArrangeDirty)
91103
{
92104
// If the the requested offsets are out-of - bounds, but we actually does have our final bounds yet,
93105
// we allow to set the desired offsets. If needed, they will then be clamped by the OnAfterArrange().
94106
// This is needed to allow a ScrollTo before the SV has been layouted.
95107

96108
_pendingChangeView = (horizontalOffset, verticalOffset, disableAnimation);
97-
_scrollableContainer.SetContentOffset(desiredOffsets, !disableAnimation);
109+
_scrollableContainer.ApplyContentOffset(desiredOffsets, !disableAnimation);
98110
}
99111
else
100112
{
101-
_scrollableContainer.SetContentOffset(clampedOffsets, !disableAnimation);
102-
}
103-
104-
if(zoomFactor is { } zoom)
105-
{
106-
ChangeViewZoom(zoom, disableAnimation);
113+
_scrollableContainer.ApplyContentOffset(clampedOffsets, !disableAnimation);
107114
}
108115

109116
// Return true if successfully scrolled to asked offsets
@@ -130,11 +137,6 @@ partial void OnZoomModeChangedPartial(ZoomMode zoomMode)
130137
}
131138
}
132139

133-
private void ChangeViewZoom(float zoomFactor, bool disableAnimation)
134-
{
135-
_scrollableContainer?.SetZoomScale(zoomFactor, animated: !disableAnimation);
136-
}
137-
138140
private void UpdateZoomedContentAlignment()
139141
{
140142
if (ZoomFactor != 1 && Content is IFrameworkElement fe)

src/Uno.UI/UI/Xaml/Controls/TextBox/MultilineTextBoxView.iOS.cs

Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -30,6 +30,30 @@ public partial class MultilineTextBoxView : UITextView, ITextBoxView, Dependency
3030

3131
CGPoint IUIScrollView.UpperScrollLimit { get { return (CGPoint)(ContentSize - Frame.Size); } }
3232

33+
void IUIScrollView.ApplyZoomScale(nfloat scale, bool animated)
34+
{
35+
if (animated)
36+
{
37+
SetZoomScale(scale, animated);
38+
}
39+
else
40+
{
41+
ZoomScale = scale;
42+
}
43+
}
44+
45+
void IUIScrollView.ApplyContentOffset(CGPoint contentOffset, bool animated)
46+
{
47+
if (animated)
48+
{
49+
SetContentOffset(contentOffset, animated);
50+
}
51+
else
52+
{
53+
ContentOffset = contentOffset;
54+
}
55+
}
56+
3357
public MultilineTextBoxView(TextBox textBox)
3458
{
3559
_textBox = new WeakReference<TextBox>(textBox);

0 commit comments

Comments
 (0)