[iOS] Protect against SetNativeControl when no Element is present (#6415) fixes #6368 fixes #6328

* [Controls] Add repo for issue #6368 and #6328

* [iOS] Protect against no Element from CustomRenderers bad usage

* [Controls] Drop private on sample code
This commit is contained in:
Rui Marinho 2019-06-13 14:21:09 +01:00 коммит произвёл GitHub
Родитель 216a6333f1
Коммит 7712162fb7
Не найден ключ, соответствующий данной подписи
Идентификатор ключа GPG: 4AEE18F83AFDEB23
7 изменённых файлов: 176 добавлений и 6 удалений

Просмотреть файл

@ -0,0 +1,41 @@
using System;
using UIKit;
using Xamarin.Forms;
using Xamarin.Forms.ControlGallery.iOS;
using Xamarin.Forms.Platform.iOS;
using static Xamarin.Forms.Controls.Issues.Issue6368;
[assembly: ExportRenderer(typeof(CustomView), typeof(CustomRenderer))]
namespace Xamarin.Forms.ControlGallery.iOS
{
public class CustomRenderer : ViewRenderer<CustomView, UIView>
{
protected override void OnElementChanged(ElementChangedEventArgs<CustomView> e)
{
base.OnElementChanged(e);
//
// --- Important --- //
//
// This is a WRONG Pattern!
//Pattern taken from: https://docs.microsoft.com/en-us/xamarin/xamarin-forms/app-fundamentals/custom-renderer/view
if (this.Control == null)
{
// Instantiate the native control and assign it to the Control property with
// the SetNativeControl method
UIView myView = new UIView();
this.SetNativeControl(myView);
}
if (e.OldElement != null)
{
// Unsubscribe from event handlers and cleanup any resources
}
if (e.NewElement != null)
{
// Configure the control and subscribe to event handlers
}
}
}
}

Просмотреть файл

@ -0,0 +1,45 @@
using System;
using UIKit;
using Xamarin.Forms;
using Xamarin.Forms.ControlGallery.iOS.CustomRenderers;
using Xamarin.Forms.Platform.iOS;
using static Xamarin.Forms.Controls.Issues.Issue6368;
[assembly: ExportRenderer(typeof(RoundedLabel), typeof(RoundedLabelRenderer))]
namespace Xamarin.Forms.ControlGallery.iOS.CustomRenderers
{
public class RoundedLabelRenderer : LabelRenderer
{
protected override void OnElementChanged(ElementChangedEventArgs<Label> e)
{
if (Control == null)
{
SetNativeControl(new TagUiLabel());
}
base.OnElementChanged(e);
if (e.NewElement != null)
{
this.Layer.CornerRadius = 10;
this.Layer.BorderColor = ColorExtensions.ToCGColor(ColorExtensions.ToColor(UIColor.FromRGB(3, 169, 244)));
this.Layer.BackgroundColor = ColorExtensions.ToCGColor(Color.GhostWhite);
this.Layer.BorderWidth = 1;
}
}
}
class TagUiLabel : UILabel
{
UIEdgeInsets _edgeInsets = new UIEdgeInsets(10, 5, 10, 5);
UIEdgeInsets _inverseEdgeInsets = new UIEdgeInsets(-10, -5, -10, -5);
public override CoreGraphics.CGRect TextRectForBounds(CoreGraphics.CGRect bounds, nint numberOfLines)
{
var textRect = base.TextRectForBounds(_edgeInsets.InsetRect(bounds), numberOfLines);
return _inverseEdgeInsets.InsetRect(textRect);
}
public override void DrawText(CoreGraphics.CGRect rect)
{
base.DrawText(_edgeInsets.InsetRect(rect));
}
}
}

Просмотреть файл

@ -117,6 +117,8 @@
<Compile Include="CustomRenderers.cs" />
<Compile Include="CustomRendererBugzila38731.cs" />
<Compile Include="CustomApplication.cs" />
<Compile Include="CustomRenderers\CustomRenderer.cs" />
<Compile Include="CustomRenderers\RoundedLabelRenderer.cs" />
</ItemGroup>
<ItemGroup>
<ProjectReference Include="..\Xamarin.Forms.Controls\Xamarin.Forms.Controls.csproj">
@ -386,6 +388,7 @@
</ItemGroup>
<ItemGroup>
<Folder Include="Resources\Fonts\" />
<Folder Include="CustomRenderers\" />
</ItemGroup>
<Import Project="$(MSBuildExtensionsPath)\Xamarin\iOS\Xamarin.iOS.CSharp.targets" />
</Project>

Просмотреть файл

@ -0,0 +1,67 @@
using Xamarin.Forms.CustomAttributes;
using Xamarin.Forms.Internals;
#if UITEST
using Xamarin.Forms.Core.UITests;
using Xamarin.UITest;
using NUnit.Framework;
#endif
namespace Xamarin.Forms.Controls.Issues
{
#if UITEST
[Category(UITestCategories.CustomRenderers)]
#endif
[Preserve(AllMembers = true)]
[Issue(IssueTracker.Github, 6368, "[CustomRenderer]Crash when navigating back from page with custom renderer control", PlatformAffected.iOS)]
public class Issue6368 : TestNavigationPage
{
public class CustomView : View
{
}
public class RoundedLabel : Label
{
}
protected override void Init()
{
var rootPage = new ContentPage();
var button = new Button()
{
AutomationId = "btnGo",
Text = "Click me to go to the next page",
Command = new Command(() => PushAsync(new ContentPage()
{
Content = GetContent()
}))
};
var content = GetContent();
content.Children.Add(button);
rootPage.Content = content;
PushAsync(rootPage);
}
static StackLayout GetContent()
{
var content2 = new StackLayout();
content2.Children.Add(new RoundedLabel { Text = "Go to next Page" });
content2.Children.Add(new RoundedLabel { Text = "then navigate back" });
content2.Children.Add(new RoundedLabel { Text = "If test doesn't crash it passed" });
content2.Children.Add(new CustomView());
return content2;
}
#if UITEST && __IOS__
[Test]
public void Issue6368Test()
{
RunningApp.WaitForElement (q => q.Marked ("btnGo"));
RunningApp.Tap(q => q.Marked("btnGo"));
RunningApp.WaitForElement(q => q.Marked("Go to next Page"));
RunningApp.NavigateBack();
}
#endif
}
}

Просмотреть файл

@ -897,6 +897,7 @@
<DependentUpon>Issue4356.xaml</DependentUpon>
</Compile>
<Compile Include="$(MSBuildThisFileDirectory)Issue5888.cs" />
<Compile Include="$(MSBuildThisFileDirectory)Issue6368.cs" />
</ItemGroup>
<ItemGroup>
<EmbeddedResource Include="$(MSBuildThisFileDirectory)Bugzilla22229.xaml">

Просмотреть файл

@ -47,5 +47,7 @@
public const string Performance = "Performance";
public const string Visual = "Visual";
public const string AppLinks = "AppLinks";
public const string Shell = "Shell";
public const string CustomRenderers = "CustomRenderers";
}
}

Просмотреть файл

@ -40,7 +40,7 @@ namespace Xamarin.Forms.Platform.MacOS
event EventHandler _controlChanging;
event EventHandler _controlChanged;
bool IsElementOrControlEmpty => Element == null || Control == null;
protected virtual TNativeView CreateNativeControl()
{
@ -188,7 +188,7 @@ namespace Xamarin.Forms.Platform.MacOS
protected override void SetBackgroundColor(Color color)
{
if (Control == null)
if (IsElementOrControlEmpty)
return;
#if __MOBILE__
if (color == Color.Default)
@ -217,8 +217,7 @@ namespace Xamarin.Forms.Platform.MacOS
#endif
Control = uiview;
if (Element.BackgroundColor != Color.Default)
SetBackgroundColor(Element.BackgroundColor);
UpdateBackgroundColor();
UpdateIsEnabled();
@ -236,9 +235,18 @@ namespace Xamarin.Forms.Platform.MacOS
}
#endif
void UpdateBackgroundColor()
{
if (IsElementOrControlEmpty)
return;
if (Element.BackgroundColor != Color.Default)
SetBackgroundColor(Element.BackgroundColor);
}
void UpdateIsEnabled()
{
if (Element == null || Control == null)
if (IsElementOrControlEmpty)
return;
var uiControl = Control as NativeControl;
@ -249,6 +257,9 @@ namespace Xamarin.Forms.Platform.MacOS
void UpdateFlowDirection()
{
if (IsElementOrControlEmpty)
return;
Control.UpdateFlowDirection(Element);
}
@ -260,4 +271,4 @@ namespace Xamarin.Forms.Platform.MacOS
focusRequestArgs.Result = focusRequestArgs.Focus ? Control.BecomeFirstResponder() : Control.ResignFirstResponder();
}
}
}
}