[uikit] Fix contest between UITextField.Ended[WithReason] events. Fixes #53174 (#1875)

iOS 10 added a new `textFieldDidEndEditing:reason:` to `UITextField`. It
gets called, if set, before the older `textFieldDidEndEditing`.

This cause a problem for code that does add events for both since the
internal `*Delegate` does override both (to allow events) so the call
might be "lost" without additional logic.

This fix makes sure that even using one (or the two) events will work
across all versions of iOS.

reference:
https://bugzilla.xamarin.com/show_bug.cgi?id=53174
This commit is contained in:
Sebastien Pouliot 2017-03-15 07:44:01 -05:00 коммит произвёл GitHub
Родитель 2380389278
Коммит 7f6f2bf81d
3 изменённых файлов: 204 добавлений и 2 удалений

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

@ -1969,6 +1969,7 @@ namespace XamCore.UIKit {
[iOS (10,0), TV (10,0), NoWatch]
[Native]
public enum UITextFieldDidEndEditingReason : nint {
Unknown = -1, // helper value (not in headers)
Committed,
[NoiOS]
Cancelled

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

@ -7,10 +7,210 @@
// Copyright 2009, Novell, Inc.
//
#if !WATCH
#if !WATCH && !COREBUILD
using System;
using XamCore.Foundation;
using XamCore.ObjCRuntime;
namespace XamCore.UIKit {
public partial class UITextFieldEditingEndedEventArgs : EventArgs {
public UITextFieldEditingEndedEventArgs (UITextFieldDidEndEditingReason reason)
{
this.Reason = reason;
}
public UITextFieldDidEndEditingReason Reason { get; set; }
}
public delegate bool UITextFieldChange (UITextField textField, NSRange range, string replacementString);
public delegate bool UITextFieldCondition (UITextField textField);
public partial class UITextField : IUITextInputTraits {
internal virtual Type GetInternalEventDelegateType
{
get { return typeof (_UITextFieldDelegate); }
}
internal virtual _UITextFieldDelegate CreateInternalEventDelegateType ()
{
return (_UITextFieldDelegate)(new _UITextFieldDelegate());
}
internal _UITextFieldDelegate EnsureUITextFieldDelegate ()
{
#if XAMCORE_2_0
if (Delegate != null)
UIApplication.EnsureEventAndDelegateAreNotMismatched (Delegate, GetInternalEventDelegateType);
_UITextFieldDelegate del = Delegate as _UITextFieldDelegate;
if (del == null){
del = (_UITextFieldDelegate)CreateInternalEventDelegateType ();
Delegate = (IUITextFieldDelegate)del;
}
return del;
#else
var del = Delegate;
if (del == null || (!(del is _UITextFieldDelegate))){
del = new _UITextFieldDelegate ();
Delegate = del;
}
return (_UITextFieldDelegate) del;
#endif
}
#pragma warning disable 672
[Register]
#if XAMCORE_2_0
internal class _UITextFieldDelegate : NSObject, IUITextFieldDelegate {
#else
internal class _UITextFieldDelegate : UITextFieldDelegate {
#endif
public _UITextFieldDelegate () { IsDirectBinding = false; }
internal EventHandler editingEnded;
[Preserve (Conditional = true)]
[Export ("textFieldDidEndEditing:")]
public void EditingEnded (UITextField textField)
{
EventHandler handler = editingEnded;
if (handler != null){
handler (textField, EventArgs.Empty);
} else {
// if this is executed before iOS10 and only the new API is used we'll raise the new event (if set)
EventHandler<UITextFieldEditingEndedEventArgs> handler2 = editingEnded1;
if (handler2 != null) {
var args = new UITextFieldEditingEndedEventArgs (UITextFieldDidEndEditingReason.Unknown);
handler2 (textField, args);
}
}
}
internal EventHandler<UITextFieldEditingEndedEventArgs> editingEnded1;
[Preserve (Conditional = true)]
[Export ("textFieldDidEndEditing:reason:")]
public void EditingEnded (UITextField textField, UITextFieldDidEndEditingReason reason)
{
EventHandler<UITextFieldEditingEndedEventArgs> handler = editingEnded1;
if (handler != null) {
var args = new UITextFieldEditingEndedEventArgs (reason);
handler (textField, args);
} else {
// if this is executed on iOS10 (or late) and only the old API is used then we'll raise the old event (if set)
EventHandler handler2 = editingEnded;
if (handler2 != null)
handler2 (textField, EventArgs.Empty);
}
}
internal EventHandler editingStarted;
[Preserve (Conditional = true)]
[Export ("textFieldDidBeginEditing:")]
public void EditingStarted (UITextField textField)
{
EventHandler handler = editingStarted;
if (handler != null){
handler (textField, EventArgs.Empty);
}
}
internal UITextFieldCondition shouldBeginEditing;
[Preserve (Conditional = true)]
[Export ("textFieldShouldBeginEditing:")]
public bool ShouldBeginEditing (UITextField textField)
{
UITextFieldCondition handler = shouldBeginEditing;
if (handler != null)
return handler (textField);
return true;
}
internal UITextFieldChange shouldChangeCharacters;
[Preserve (Conditional = true)]
[Export ("textField:shouldChangeCharactersInRange:replacementString:")]
public bool ShouldChangeCharacters (UITextField textField, NSRange range, string replacementString)
{
UITextFieldChange handler = shouldChangeCharacters;
if (handler != null)
return handler (textField, range, replacementString);
return true;
}
internal UITextFieldCondition shouldClear;
[Preserve (Conditional = true)]
[Export ("textFieldShouldClear:")]
public bool ShouldClear (UITextField textField)
{
UITextFieldCondition handler = shouldClear;
if (handler != null)
return handler (textField);
return true;
}
internal UITextFieldCondition shouldEndEditing;
[Preserve (Conditional = true)]
[Export ("textFieldShouldEndEditing:")]
public bool ShouldEndEditing (UITextField textField)
{
UITextFieldCondition handler = shouldEndEditing;
if (handler != null)
return handler (textField);
return true;
}
internal UITextFieldCondition shouldReturn;
[Preserve (Conditional = true)]
[Export ("textFieldShouldReturn:")]
public bool ShouldReturn (UITextField textField)
{
UITextFieldCondition handler = shouldReturn;
if (handler != null)
return handler (textField);
return true;
}
}
#pragma warning restore 672
public event EventHandler Ended {
add { EnsureUITextFieldDelegate ().editingEnded += value; }
remove { EnsureUITextFieldDelegate ().editingEnded -= value; }
}
public event EventHandler<UITextFieldEditingEndedEventArgs> EndedWithReason {
add { EnsureUITextFieldDelegate ().editingEnded1 += value; }
remove { EnsureUITextFieldDelegate ().editingEnded1 -= value; }
}
public event EventHandler Started {
add { EnsureUITextFieldDelegate ().editingStarted += value; }
remove { EnsureUITextFieldDelegate ().editingStarted -= value; }
}
public UITextFieldCondition ShouldBeginEditing {
get { return EnsureUITextFieldDelegate ().shouldBeginEditing; }
set { EnsureUITextFieldDelegate ().shouldBeginEditing = value; }
}
public UITextFieldChange ShouldChangeCharacters {
get { return EnsureUITextFieldDelegate ().shouldChangeCharacters; }
set { EnsureUITextFieldDelegate ().shouldChangeCharacters = value; }
}
public UITextFieldCondition ShouldClear {
get { return EnsureUITextFieldDelegate ().shouldClear; }
set { EnsureUITextFieldDelegate ().shouldClear = value; }
}
public UITextFieldCondition ShouldEndEditing {
get { return EnsureUITextFieldDelegate ().shouldEndEditing; }
set { EnsureUITextFieldDelegate ().shouldEndEditing = value; }
}
public UITextFieldCondition ShouldReturn {
get { return EnsureUITextFieldDelegate ().shouldReturn; }
set { EnsureUITextFieldDelegate ().shouldReturn = value; }
}
}
}

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

@ -11678,7 +11678,8 @@ namespace XamCore.UIKit {
UITableViewRowAction Create (UITableViewRowActionStyle style, [NullAllowed] string title, Action<UITableViewRowAction, NSIndexPath> handler);
}
[BaseType (typeof (UIControl), Delegates=new string [] { "WeakDelegate" }, Events=new Type [] {typeof(UITextFieldDelegate)})]
[BaseType (typeof (UIControl), Delegates=new string [] { "WeakDelegate" })]
// , Events=new Type [] {typeof(UITextFieldDelegate)})] custom logic needed, see https://bugzilla.xamarin.com/show_bug.cgi?id=53174
interface UITextField : UITextInput, UIContentSizeCategoryAdjusting {
[Export ("initWithFrame:")]
IntPtr Constructor (CGRect frame);