Skip to content

Commit

Permalink
Use the CrossPlatformLayout similar to MauiView
Browse files Browse the repository at this point in the history
  • Loading branch information
tj-devel709 committed Jul 2, 2024
1 parent 3e91a0f commit be6e2a1
Show file tree
Hide file tree
Showing 10 changed files with 123 additions and 45 deletions.
35 changes: 15 additions & 20 deletions src/Controls/src/Core/Button/Button.iOS.cs
Original file line number Diff line number Diff line change
Expand Up @@ -8,10 +8,11 @@
using UIKit;
using CoreGraphics;
using Microsoft.Extensions.Logging;
using Microsoft.Maui.Layouts;

namespace Microsoft.Maui.Controls
{
public partial class Button
public partial class Button : ICrossPlatformLayout
{
CGSize _originalImageSize = CGSize.Empty;

Expand All @@ -23,8 +24,8 @@ public partial class Button
/// <param name="widthConstraint"></param>
/// <param name="heightConstraint"></param>
/// <returns>Returns a <see cref="Size"/> representing the width and height of the button.</returns>
/// <remarks>Calling base.MeasureOverride will call SizeThatFits() on the UIButton's UIView but will not consider some MAUI Button elements such as the BorderWidth and image placement. Instead we calculate that manually.</remarks>
protected override Size MeasureOverride(double widthConstraint, double heightConstraint)
/// <remarks>This method is used to pseudo-override the SizeThatFits() on the UIButton since we cannot override the UIButton class.</remarks>
Size ICrossPlatformLayout.CrossPlatformMeasure(double widthConstraint, double heightConstraint)
{
var button = this;

Expand All @@ -48,11 +49,8 @@ protected override Size MeasureOverride(double widthConstraint, double heightCon
if (padding.IsNaN)
padding = ButtonHandler.DefaultPadding;

var buttonWidthConstraint = button.WidthRequest == -1 ? widthConstraint : Math.Min(button.WidthRequest, widthConstraint);
var buttonHeightConstraint = button.HeightRequest == -1 ? heightConstraint : Math.Min(button.HeightRequest, heightConstraint);

var titleWidthConstraint = buttonWidthConstraint - padding.Left - padding.Right - borderWidth * 2;
var titleHeightConstraint = buttonHeightConstraint - padding.Top - padding.Bottom - borderWidth * 2;
var titleWidthConstraint = widthConstraint - padding.Left - padding.Right - borderWidth * 2;
var titleHeightConstraint = heightConstraint - padding.Top - padding.Bottom - borderWidth * 2;

var image = platformButton.CurrentImage;

Expand All @@ -65,7 +63,7 @@ protected override Size MeasureOverride(double widthConstraint, double heightCon
}

// Resize the image if necessary and then update the image variable
if (ResizeImageIfNecessary(platformButton, button, image, 0, padding, borderWidth, buttonWidthConstraint, buttonHeightConstraint, _originalImageSize))
if (ResizeImageIfNecessary(platformButton, button, image, 0, padding, borderWidth, widthConstraint, heightConstraint, _originalImageSize))
{
image = platformButton.CurrentImage;
}
Expand Down Expand Up @@ -134,12 +132,8 @@ protected override Size MeasureOverride(double widthConstraint, double heightCon
}
}

var returnSize = new Size(button.WidthRequest == -1 ? Math.Min(buttonContentWidth, buttonWidthConstraint) : button.WidthRequest,
button.HeightRequest == -1 ? Math.Min(buttonContentHeight, buttonHeightConstraint) : button.HeightRequest);

// Add the margins to the return size
returnSize.Width += (nfloat)button.Margin.HorizontalThickness;
returnSize.Height += (nfloat)button.Margin.VerticalThickness;
var returnSize = new Size(Math.Min(buttonContentWidth, widthConstraint),
Math.Min(buttonContentHeight, heightConstraint));

// Rounding the values up to the nearest whole number to match UIView.SizeThatFits
return new Size((int)Math.Ceiling(returnSize.Width), (int)Math.Ceiling(returnSize.Height));
Expand All @@ -149,16 +143,17 @@ protected override Size MeasureOverride(double widthConstraint, double heightCon
/// Arrange the button and layout the image and title.
/// </summary>
/// <param name="bounds"></param>
/// <returns></returns>
protected override Size ArrangeOverride(Rect bounds)
/// <returns>Returns a <see cref="Size"/> representing the width and height of the button.</returns>
Size ICrossPlatformLayout.CrossPlatformArrange(Rect bounds)
{
var button = this;
bounds = this.ComputeFrame(bounds);

var platformButton = Handler?.PlatformView as UIButton;

// Layout the image and title of the button
LayoutButton(platformButton, button, bounds);
LayoutButton(platformButton, this, bounds);

return base.ArrangeOverride(bounds);
return new Size(bounds.Width, bounds.Height);
}

/// <summary>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -7447,7 +7447,6 @@ override Microsoft.Maui.Controls.Application.OnParentSet() -> void
override Microsoft.Maui.Controls.Border.OnPropertyChanged(string? propertyName = null) -> void
override Microsoft.Maui.Controls.BoxView.OnMeasure(double widthConstraint, double heightConstraint) -> Microsoft.Maui.SizeRequest
override Microsoft.Maui.Controls.BoxView.OnPropertyChanged(string? propertyName = null) -> void
override Microsoft.Maui.Controls.Button.ArrangeOverride(Microsoft.Maui.Graphics.Rect bounds) -> Microsoft.Maui.Graphics.Size
override Microsoft.Maui.Controls.Button.ChangeVisualState() -> void
override Microsoft.Maui.Controls.Button.OnBindingContextChanged() -> void
override Microsoft.Maui.Controls.Cell.OnBindingContextChanged() -> void
Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,4 @@
#nullable enable
override Microsoft.Maui.Controls.Button.MeasureOverride(double widthConstraint, double heightConstraint) -> Microsoft.Maui.Graphics.Size
override Microsoft.Maui.Controls.Handlers.Compatibility.CellRenderer.Invoke(string! command, object? args) -> void
override Microsoft.Maui.Controls.Handlers.Compatibility.CellRenderer.UpdateValue(string! property) -> void
override Microsoft.Maui.Controls.Handlers.Compatibility.ShellRenderer.PreferredStatusBarUpdateAnimation.get -> UIKit.UIStatusBarAnimation
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -7447,7 +7447,6 @@ override Microsoft.Maui.Controls.Application.OnParentSet() -> void
override Microsoft.Maui.Controls.Border.OnPropertyChanged(string? propertyName = null) -> void
override Microsoft.Maui.Controls.BoxView.OnMeasure(double widthConstraint, double heightConstraint) -> Microsoft.Maui.SizeRequest
override Microsoft.Maui.Controls.BoxView.OnPropertyChanged(string? propertyName = null) -> void
override Microsoft.Maui.Controls.Button.ArrangeOverride(Microsoft.Maui.Graphics.Rect bounds) -> Microsoft.Maui.Graphics.Size
override Microsoft.Maui.Controls.Button.ChangeVisualState() -> void
override Microsoft.Maui.Controls.Button.OnBindingContextChanged() -> void
override Microsoft.Maui.Controls.Cell.OnBindingContextChanged() -> void
Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,4 @@
#nullable enable
override Microsoft.Maui.Controls.Button.MeasureOverride(double widthConstraint, double heightConstraint) -> Microsoft.Maui.Graphics.Size
override Microsoft.Maui.Controls.Handlers.Compatibility.CellRenderer.Invoke(string! command, object? args) -> void
override Microsoft.Maui.Controls.Handlers.Compatibility.CellRenderer.UpdateValue(string! property) -> void
override Microsoft.Maui.Controls.GradientBrush.IsEmpty.get -> bool
Expand Down
9 changes: 9 additions & 0 deletions src/Core/src/Handlers/Button/ButtonHandler.iOS.cs
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,15 @@ protected override UIButton CreatePlatformView()

readonly ButtonEventProxy _proxy = new ButtonEventProxy();

protected override void SetupContainer()
{
base.SetupContainer();
if (ContainerView is WrapperView wrapperView)
{
wrapperView.CrossPlatformLayout = VirtualView as ICrossPlatformLayout;
}
}

protected override void ConnectHandler(UIButton platformView)
{
_proxy.Connect(VirtualView, platformView);
Expand Down
2 changes: 1 addition & 1 deletion src/Core/src/Handlers/ViewHandlerExtensions.iOS.cs
Original file line number Diff line number Diff line change
Expand Up @@ -98,7 +98,7 @@ internal static Size GetDesiredSizeFromHandler(this IViewHandler viewHandler, do
}
else if (platformView is WrapperView wrapper)
{
sizeThatFits = wrapper.SizeThatFitsWrapper(new CGSize((float)widthConstraint, (float)heightConstraint), virtualView.Width, virtualView.Height);
sizeThatFits = wrapper.SizeThatFitsWrapper(new CGSize((float)widthConstraint, (float)heightConstraint), virtualView.Width, virtualView.Height, virtualView);
}
else
{
Expand Down
115 changes: 95 additions & 20 deletions src/Core/src/Platform/iOS/WrapperView.cs
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,17 @@ namespace Microsoft.Maui.Platform
{
public partial class WrapperView : UIView, IDisposable, IUIViewLifeCycleEvents
{
WeakReference<ICrossPlatformLayout>? _crossPlatformLayoutReference;

internal ICrossPlatformLayout? CrossPlatformLayout
{
get => _crossPlatformLayoutReference != null && _crossPlatformLayoutReference.TryGetTarget(out var v) ? v : null;
set => _crossPlatformLayoutReference = value == null ? null : new WeakReference<ICrossPlatformLayout>(value);
}

double _lastMeasureHeight = double.NaN;
double _lastMeasureWidth = double.NaN;

CAShapeLayer? _maskLayer;
CAShapeLayer? _backgroundMaskLayer;
CAShapeLayer? _shadowLayer;
Expand All @@ -26,6 +37,19 @@ public WrapperView(CGRect frame)
{
}

internal bool IsMeasureValid(double widthConstraint, double heightConstraint)
{
// Check the last constraints this View was measured with; if they're the same,
// then the current measure info is already correct and we don't need to repeat it
return heightConstraint == _lastMeasureHeight && widthConstraint == _lastMeasureWidth;
}

internal void CacheMeasureConstraints(double widthConstraint, double heightConstraint)
{
_lastMeasureWidth = widthConstraint;
_lastMeasureHeight = heightConstraint;
}

CAShapeLayer? MaskLayer
{
get => _maskLayer;
Expand Down Expand Up @@ -103,6 +127,17 @@ public override void LayoutSubviews()
SetClip();
SetShadow();
SetBorder();

var boundWidth = Bounds.Width;
var boundHeight = Bounds.Height;

if (!IsMeasureValid(boundWidth, boundHeight))
{
CrossPlatformLayout?.CrossPlatformMeasure(boundWidth, boundHeight);
CacheMeasureConstraints(boundWidth, boundHeight);
}

CrossPlatformLayout?.CrossPlatformArrange(Bounds.ToRectangle());
}

public new void Dispose()
Expand All @@ -117,41 +152,81 @@ public override void LayoutSubviews()
public override CGSize SizeThatFits(CGSize size)
{
var subviews = Subviews;
if (subviews.Length == 0)
return base.SizeThatFits(size);
CGSize returnSize;

var child = subviews[0];

// Calling SizeThatFits on an ImageView always returns the image's dimensions, so we need to call the extension method
// This also affects ImageButtons
if (child is UIImageView imageView)
if (subviews.Length == 0)
{
return imageView.SizeThatFitsImage(size);
returnSize = base.SizeThatFits(size);
}
else if (child is UIButton imageButton && imageButton.ImageView?.Image is not null && imageButton.CurrentTitle is null)

else
{
return imageButton.ImageView.SizeThatFitsImage(size);
var child = subviews[0];

// Calling SizeThatFits on an ImageView always returns the image's dimensions, so we need to call the extension method
// This also affects ImageButtons
if (child is UIImageView imageView)
{
returnSize = imageView.SizeThatFitsImage(size);
}
else if (child is UIButton imageButton && imageButton.ImageView?.Image is not null && imageButton.CurrentTitle is null)
{
returnSize = imageButton.ImageView.SizeThatFitsImage(size);
}
else if (CrossPlatformLayout is not null)
{
returnSize = CrossPlatformLayout.CrossPlatformMeasure(size.Width, size.Height).ToCGSize();
}
else
{
returnSize = child.SizeThatFits(size);
}
}

return child.SizeThatFits(size);
CacheMeasureConstraints(size.Width, size.Height);
return returnSize;
}

internal CGSize SizeThatFitsWrapper(CGSize originalSpec, double virtualViewWidth, double virtualViewHeight)
internal CGSize SizeThatFitsWrapper(CGSize originalSpec, double virtualViewWidth, double virtualViewHeight, IView view)
{
var subviews = Subviews;
if (subviews.Length == 0)
return base.SizeThatFits(originalSpec);
CGSize returnSize;
var widthConstraint = IsExplicitSet(virtualViewWidth) ? virtualViewWidth : originalSpec.Width;
var heightConstraint = IsExplicitSet(virtualViewHeight) ? virtualViewHeight : originalSpec.Height;

var child = subviews[0];
if (subviews.Length == 0)
{
returnSize = base.SizeThatFits(originalSpec);
}

if (child is UIImageView || (child is UIButton imageButton && imageButton.ImageView?.Image is not null && imageButton.CurrentTitle is null))
else
{
var widthConstraint = IsExplicitSet(virtualViewWidth) ? virtualViewWidth : originalSpec.Width;
var heightConstraint = IsExplicitSet(virtualViewHeight) ? virtualViewHeight : originalSpec.Height;
return SizeThatFits(new CGSize(widthConstraint, heightConstraint));
var child = subviews[0];

if (child is UIImageView || (child is UIButton imageButton && imageButton.ImageView?.Image is not null && imageButton.CurrentTitle is null))
{
if(CrossPlatformLayout is not null)
{
returnSize = CrossPlatformLayout.CrossPlatformMeasure(widthConstraint, heightConstraint);
}
else
{
returnSize = SizeThatFits(new CGSize(widthConstraint, heightConstraint));
}
}

else if (CrossPlatformLayout is not null)
{
returnSize = CrossPlatformLayout.CrossPlatformMeasure(widthConstraint, heightConstraint);
}
else
{
returnSize = SizeThatFits(originalSpec);
}
}

return SizeThatFits(originalSpec);
CacheMeasureConstraints(widthConstraint, heightConstraint);
return returnSize;
}

public override void SetNeedsLayout()
Expand Down
2 changes: 2 additions & 0 deletions src/Core/src/PublicAPI/net-ios/PublicAPI.Unshipped.txt
Original file line number Diff line number Diff line change
Expand Up @@ -49,6 +49,7 @@ Microsoft.Maui.Platform.MauiScrollView
Microsoft.Maui.Platform.MauiScrollView.MauiScrollView() -> void
Microsoft.Maui.SoftInputExtensions
override Microsoft.Maui.Handlers.ButtonHandler.NeedsContainer.get -> bool
override Microsoft.Maui.Handlers.ButtonHandler.SetupContainer() -> void
override Microsoft.Maui.Handlers.ContentViewHandler.DisconnectHandler(Microsoft.Maui.Platform.ContentView! platformView) -> void
override Microsoft.Maui.Handlers.RefreshViewHandler.SetVirtualView(Microsoft.Maui.IView! view) -> void
override Microsoft.Maui.Handlers.ScrollViewHandler.NeedsContainer.get -> bool
Expand Down Expand Up @@ -159,4 +160,5 @@ Microsoft.Maui.Platform.MauiView.IsMeasureValid(double widthConstraint, double h
Microsoft.Maui.Platform.UIEdgeInsetsExtensions
static Microsoft.Maui.Platform.UIEdgeInsetsExtensions.ToThickness(this UIKit.UIEdgeInsets insets) -> Microsoft.Maui.Thickness
override Microsoft.Maui.Handlers.BorderHandler.PlatformArrange(Microsoft.Maui.Graphics.Rect rect) -> void
*REMOVED*override Microsoft.Maui.Platform.MauiLabel.InvalidateIntrinsicContentSize() -> voidrect) -> void
*REMOVED*override Microsoft.Maui.Platform.MauiLabel.InvalidateIntrinsicContentSize() -> void
Original file line number Diff line number Diff line change
Expand Up @@ -50,6 +50,7 @@ Microsoft.Maui.Platform.MauiScrollView
Microsoft.Maui.Platform.MauiScrollView.MauiScrollView() -> void
Microsoft.Maui.Platform.KeyboardAutoManagerScroll
Microsoft.Maui.SoftInputExtensions
override Microsoft.Maui.Handlers.ButtonHandler.SetupContainer() -> void
override Microsoft.Maui.Handlers.BorderHandler.PlatformArrange(Microsoft.Maui.Graphics.Rect rect) -> void
override Microsoft.Maui.Handlers.ButtonHandler.NeedsContainer.get -> bool
override Microsoft.Maui.Handlers.ContentViewHandler.DisconnectHandler(Microsoft.Maui.Platform.ContentView! platformView) -> void
Expand Down

0 comments on commit be6e2a1

Please sign in to comment.