iOS: Frame with HasShadow set to true and BackgroundColor alpha < 1 casts shadow...
authorPieter Nijs <pieternijs@live.be>
Tue, 29 Oct 2019 14:09:19 +0000 (15:09 +0100)
committerGerald Versluis <gerald.versluis@microsoft.com>
Tue, 29 Oct 2019 14:09:19 +0000 (15:09 +0100)
* Updated renderer with additional ShadowView

* Added buttons to test some behavior

* ShadowLayer background color tweaks

* Resolved flawed merge, restored _Template page

* Apply suggestions from code review

Co-Authored-By: Gerald Versluis <github@geraldversluis.nl>
* Added more descriptive issue Title

* Updated Issue5108.xml's properties

* Update Xamarin.Forms.Controls.Issues.Shared.projitems

Fixed missing closing tags

* Updated projitems

* Removed eventhandlers from XAML

* Update Issue5108.xaml.cs

Added eventhandlers in code + surrounded code-behind with APP preprocessor directive to avoid issues when building for UITEST

Xamarin.Forms.Controls.Issues/Xamarin.Forms.Controls.Issues.Shared/Issue5108.xaml [new file with mode: 0644]
Xamarin.Forms.Controls.Issues/Xamarin.Forms.Controls.Issues.Shared/Issue5108.xaml.cs [new file with mode: 0644]
Xamarin.Forms.Controls.Issues/Xamarin.Forms.Controls.Issues.Shared/Xamarin.Forms.Controls.Issues.Shared.projitems
Xamarin.Forms.Controls.Issues/Xamarin.Forms.Controls.Issues.Shared/_TemplateMarkup.xaml.cs
Xamarin.Forms.Platform.iOS/Renderers/FrameRenderer.cs

diff --git a/Xamarin.Forms.Controls.Issues/Xamarin.Forms.Controls.Issues.Shared/Issue5108.xaml b/Xamarin.Forms.Controls.Issues/Xamarin.Forms.Controls.Issues.Shared/Issue5108.xaml
new file mode 100644 (file)
index 0000000..f97917d
--- /dev/null
@@ -0,0 +1,31 @@
+<?xml version="1.0" encoding="utf-8" ?>
+<controls:TestContentPage
+    xmlns="http://xamarin.com/schemas/2014/forms"
+    xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
+    xmlns:controls="clr-namespace:Xamarin.Forms.Controls"
+    x:Class="Xamarin.Forms.Controls.Issues.Issue5108">
+    <ScrollView>
+        <StackLayout>
+            <Frame BackgroundColor="#2257BABE" BorderColor="#57BABE" CornerRadius="10" Margin="20" x:Name="myframe">
+                <StackLayout Spacing="0">
+                    <StackLayout Orientation="Horizontal" Margin="0, 5, 0, 20">
+                        <Label class="RadioText" HorizontalOptions="StartAndExpand" VerticalTextAlignment="Start" FontAttributes="Bold">Please review and confirm that you agree to our terms and conditions before continuing.</Label>
+                        <Button Text="View" class="Primary" HorizontalOptions="End" VerticalOptions="Start" BorderRadius="8" WidthRequest="80"></Button>
+                    </StackLayout>
+                    <StackLayout Orientation="Horizontal" Margin="0, 5, 0, 5">
+                        <Label class="RadioText" FontAttributes="Bold" VerticalTextAlignment="Center">I agree</Label>
+                        <Switch x:Name="rbTerms" HorizontalOptions="StartAndExpand" />
+                        <Button x:Name="btnContinue" StyleClass="Primary" Text="Continue" HorizontalOptions="Center" BorderRadius="8" IsEnabled="False" WidthRequest="150"></Button>
+                    </StackLayout>
+                </StackLayout>
+            </Frame>
+            <Label Text="Toggle the HasShadow property of the frame:" Style="{DynamicResource CaptionStyle}" />
+            <Button Text="Toggle HasShadow" x:Name="HasShadowButton" />
+            <Label Text="Check if the Shadow layer updates its properties following the 'shadowee' layer:" Style="{DynamicResource CaptionStyle}" />
+            <Button Text="Update Margin" x:Name="MarginButton" />
+            <Button Text="Update Corner Radius" x:Name="RadiusButton" />
+            <Label Text="Toggle the Frame's Background to see how the shadow layer reacts. A background with alpha == 1, the shadow should be darker." Style="{DynamicResource CaptionStyle}" />
+            <Button Text="Update Background" x:Name="BackgroundButton" />
+        </StackLayout>
+    </ScrollView>
+</controls:TestContentPage>
diff --git a/Xamarin.Forms.Controls.Issues/Xamarin.Forms.Controls.Issues.Shared/Issue5108.xaml.cs b/Xamarin.Forms.Controls.Issues/Xamarin.Forms.Controls.Issues.Shared/Issue5108.xaml.cs
new file mode 100644 (file)
index 0000000..96185d1
--- /dev/null
@@ -0,0 +1,62 @@
+using Xamarin.Forms.CustomAttributes;
+using Xamarin.Forms.Internals;
+using Xamarin.Forms.Xaml;
+using System;
+
+namespace Xamarin.Forms.Controls.Issues
+{
+#if APP
+       [XamlCompilation(XamlCompilationOptions.Compile)]
+       [Preserve(AllMembers = true)]
+       [Issue(IssueTracker.Github, 5108, "iOS: Frame with HasShadow set to true and BackgroundColor alpha < 1 casts shadow on all child views", PlatformAffected.iOS)]
+       public partial class Issue5108 : TestContentPage
+       {
+               public Issue5108()
+               {
+                       InitializeComponent();
+                       MarginButton.Clicked += MarginButton_Clicked;
+                       HasShadowButton.Clicked += HasShadowButton_Clicked;
+                       RadiusButton.Clicked += RadiusButton_Clicked;
+                       BackgroundButton.Clicked += BackgroundButton_Clicked;
+               }
+
+               protected override void Init()
+               {
+               }
+
+
+               void MarginButton_Clicked(object sender, EventArgs e)
+               {
+                       if (myframe.Margin.Top == 20)
+                               myframe.Margin = new Thickness(5);
+                       else
+                               myframe.Margin = new Thickness(20);
+               }
+
+               void HasShadowButton_Clicked(object sender, EventArgs e)
+               {
+                       myframe.HasShadow = !myframe.HasShadow;
+               }
+
+               void RadiusButton_Clicked(object sender, EventArgs e)
+               {
+                       if (myframe.CornerRadius == 10)
+                               myframe.CornerRadius = 20;
+                       else
+                               myframe.CornerRadius = 10;
+               }
+
+               Color? initialColor = null;
+               void BackgroundButton_Clicked(object sender, EventArgs e)
+               {
+                       if (!initialColor.HasValue)
+                               initialColor = myframe.BackgroundColor;
+
+                       if (myframe.BackgroundColor == initialColor.Value)
+                               myframe.BackgroundColor = Color.HotPink;
+                       else
+                               myframe.BackgroundColor = initialColor.Value;
+               }
+       }
+#endif
+}
index 83ef723..baee0a0 100644 (file)
@@ -46,6 +46,9 @@
     <Compile Include="$(MSBuildThisFileDirectory)Issue8008.cs" />
     <Compile Include="$(MSBuildThisFileDirectory)Issue6640.cs" />
     <Compile Include="$(MSBuildThisFileDirectory)Issue7556.cs" />
+    <Compile Include="$(MSBuildThisFileDirectory)Issue5108.xaml.cs">
+      <SubType>Code</SubType>
+    </Compile>
     <Compile Include="$(MSBuildThisFileDirectory)Issue7329.cs" />
     <Compile Include="$(MSBuildThisFileDirectory)Issue7290.cs" />
     <Compile Include="$(MSBuildThisFileDirectory)Issue7240.cs" />
     <Compile Update="$(MSBuildThisFileDirectory)Issue6254.xaml.cs">
       <DependentUpon>Issue6254.xaml</DependentUpon>
     </Compile>
+       <Compile Update="$(MSBuildThisFileDirectory)Issue5108.xaml.cs">
+      <DependentUpon>Issue5108.xaml</DependentUpon>
+       </Compile>
     <Compile Update="$(MSBuildThisFileDirectory)Issue7357.xaml.cs">
       <DependentUpon>Issue7357.xaml</DependentUpon>
     </Compile>
     </Compile>
     <Compile Update="$(MSBuildThisFileDirectory)Issue7865.xaml.cs">
       <DependentUpon>Issue7865.xaml</DependentUpon>
-    </Compile>
+       </Compile>
   </ItemGroup>
   <ItemGroup>
     <EmbeddedResource Include="$(MSBuildThisFileDirectory)Issue1455.xaml">
     </EmbeddedResource>
   </ItemGroup>
   <ItemGroup>
+    <EmbeddedResource Include="$(MSBuildThisFileDirectory)Issue5108.xaml">
+      <SubType>Designer</SubType>
+      <Generator>MSBuild:UpdateDesignTimeXaml</Generator>
+       </EmbeddedResource>
+  </ItemGroup>
+  <ItemGroup>
     <EmbeddedResource Include="$(MSBuildThisFileDirectory)Issue7357.xaml">
       <SubType>Designer</SubType>
       <Generator>MSBuild:UpdateDesignTimeXaml</Generator>
     <EmbeddedResource Include="$(MSBuildThisFileDirectory)Issue7803.xaml">
       <SubType>Designer</SubType>
       <Generator>MSBuild:UpdateDesignTimeXaml</Generator>
-    </EmbeddedResource>
+         </EmbeddedResource>
   </ItemGroup>
   <ItemGroup>
     <EmbeddedResource Include="$(MSBuildThisFileDirectory)Issue7048.xaml">
       <Generator>MSBuild:Compile</Generator>
     </EmbeddedResource>
   </ItemGroup>
-</Project>
\ No newline at end of file
+</Project>
index e9b25fb..5c48b25 100644 (file)
@@ -49,7 +49,7 @@ namespace Xamarin.Forms.Controls.Issues
        {
                public ViewModelIssue2()
                {
-                       
+
                }
        }
 
@@ -58,7 +58,7 @@ namespace Xamarin.Forms.Controls.Issues
        {
                public ModelIssue2()
                {
-                       
+
                }
        }
 }
\ No newline at end of file
index 656f369..f07d1d2 100644 (file)
@@ -1,11 +1,15 @@
 using System.ComponentModel;
 using System.Drawing;
+using CoreAnimation;
+using CoreGraphics;
 using UIKit;
 
 namespace Xamarin.Forms.Platform.iOS
 {
        public class FrameRenderer : VisualElementRenderer<Frame>
        {
+               ShadowView _shadowView;
+
                protected override void OnElementChanged(ElementChangedEventArgs<Frame> e)
                {
                        base.OnElementChanged(e);
@@ -39,26 +43,95 @@ namespace Xamarin.Forms.Platform.iOS
                        else
                                Layer.BackgroundColor = Element.BackgroundColor.ToCGColor();
 
+                       if (Element.BorderColor == Color.Default)
+                               Layer.BorderColor = UIColor.Clear.CGColor;
+                       else
+                       {
+                               Layer.BorderColor = Element.BorderColor.ToCGColor();
+                               Layer.BorderWidth = 1;
+                       }
+
                        if (Element.HasShadow)
                        {
+                               if (_shadowView == null)
+                               {
+                                       _shadowView = new ShadowView(Layer);
+                                       SetNeedsLayout();
+                               }
+                               _shadowView.UpdateBackgroundColor();
+                               _shadowView.Layer.CornerRadius = Layer.CornerRadius;
+                               _shadowView.Layer.BorderColor = Layer.BorderColor;
+                       }
+                       else
+                       {
+                               if (_shadowView != null)
+                               {
+                                       _shadowView.RemoveFromSuperview();
+                                       _shadowView.Dispose();
+                                       _shadowView = null;
+                               }
+                       }
+
+                       Layer.RasterizationScale = UIScreen.MainScreen.Scale;
+                       Layer.ShouldRasterize = true;
+               }
+
+               public override void LayoutSubviews()
+               {
+                       if (_shadowView != null)
+                       {
+                               if (_shadowView.Superview == null)
+                                       Superview.InsertSubviewBelow(_shadowView, this);
+
+                               _shadowView?.SetNeedsLayout();
+                       }
+                       base.LayoutSubviews();
+               }
+
+               class ShadowView : UIView
+               {
+                       CALayer _shadowee;
+                       CGRect _previousBounds;
+                       CGRect _previousFrame;
+
+                       public ShadowView(CALayer shadowee)
+                       {
+                               _shadowee = shadowee;
                                Layer.ShadowRadius = 5;
                                Layer.ShadowColor = UIColor.Black.CGColor;
                                Layer.ShadowOpacity = 0.8f;
                                Layer.ShadowOffset = new SizeF();
+                               Layer.BorderWidth = 1;
                        }
-                       else
-                               Layer.ShadowOpacity = 0;
 
-                       if (Element.BorderColor == Color.Default)
-                               Layer.BorderColor = UIColor.Clear.CGColor;
-                       else
+                       public void UpdateBackgroundColor()
                        {
-                               Layer.BorderColor = Element.BorderColor.ToCGColor();
-                               Layer.BorderWidth = 1;
+                               //Putting a transparent background under any shadowee having a background with alpha < 1
+                               //Giving the Shadow a background of the same color when shadowee background == 1.
+                               //The latter will result in a 'darker' shadow as you would expect from something that 
+                               //isn't transparent. This also mimics the look as it was before with non-transparent Frames.
+                               if (_shadowee.BackgroundColor.Alpha < 1) 
+                                       BackgroundColor = UIColor.Clear;
+                               else
+                                       BackgroundColor = new UIColor(_shadowee.BackgroundColor);
                        }
 
-                       Layer.RasterizationScale = UIScreen.MainScreen.Scale;
-                       Layer.ShouldRasterize = true;
+                       public override void LayoutSubviews()
+                       {
+                               if (_shadowee.Bounds != _previousBounds || _shadowee.Frame != _previousFrame)
+                               {
+                                       base.LayoutSubviews();
+                                       SetBounds();
+                               }
+                       }
+
+                       void SetBounds()
+                       {
+                               Layer.Frame = _shadowee.Frame;
+                               Layer.Bounds = _shadowee.Bounds;
+                               _previousBounds = _shadowee.Bounds;
+                               _previousFrame = _shadowee.Frame;
+                       }
                }
        }
 }
\ No newline at end of file