From fa2f01680d23a813a5b1ab7795151b6dcb0feb7f Mon Sep 17 00:00:00 2001 From: Rui Marinho Date: Fri, 30 Sep 2016 19:48:22 +0100 Subject: [iOS] Keep our native property listener around the same time we keep our proxy, check if we are KVO compliant before adding observer (#403) --- Xamarin.Forms.ControlGallery.iOS/AppDelegate.cs | 61 +++++++++++++++++----- .../Xamarin.Forms.ControlGallery.iOS.csproj | 4 +- .../NativeBindingGalleryPage.cs | 15 +++--- Xamarin.Forms.Core/NativeBindingHelpers.cs | 9 ++-- .../Extensions/UIViewExtensions.cs | 26 ++++++++- .../NativeViewPropertyListener.cs | 4 +- 6 files changed, 91 insertions(+), 28 deletions(-) diff --git a/Xamarin.Forms.ControlGallery.iOS/AppDelegate.cs b/Xamarin.Forms.ControlGallery.iOS/AppDelegate.cs index 3eb36bfc..71c5e0c6 100644 --- a/Xamarin.Forms.ControlGallery.iOS/AppDelegate.cs +++ b/Xamarin.Forms.ControlGallery.iOS/AppDelegate.cs @@ -156,7 +156,7 @@ namespace Xamarin.Forms.ControlGallery.iOS MessagingCenter.Subscribe(this, Bugzilla40911.ReadyToSetUp40911Test, SetUp40911Test); // When the native binding gallery loads up, it'll let us know so we can set up the native bindings - MessagingCenter.Subscribe(this, NativeBindingGalleryPage.ReadyForNativeBindingsMessage, AddNativeBindings); + MessagingCenter.Subscribe(this, NativeBindingGalleryPage.ReadyForNativeBindingsMessage, AddNativeBindings); LoadApplication(app); return base.FinishedLaunching(uiApplication, launchOptions); @@ -297,6 +297,12 @@ namespace Xamarin.Forms.ControlGallery.iOS uilabel.SetBinding("Text", new Binding("NativeLabel")); uilabel.SetBinding(nameof(uilabel.TextColor), new Binding("NativeLabelColor", converter: nativeColorConverter)); + var kvoSlider = new KVOUISlider(); + kvoSlider.MaxValue = 100; + kvoSlider.MinValue = 0; + kvoSlider.SetBinding(nameof(kvoSlider.KVOValue), new Binding("Age", BindingMode.TwoWay)); + sl?.Children.Add(kvoSlider); + var uiView = new UIView(new RectangleF(0, 0, width, heightCustomLabelView)); uiView.Add(uilabel); sl?.Children.Add(uiView); @@ -321,32 +327,61 @@ namespace Xamarin.Forms.ControlGallery.iOS page.Layout.Children.Add(button); } - public void StartPressed40911 () - { + public void StartPressed40911() + { var loginViewController = new UIViewController { View = { BackgroundColor = UIColor.White } }; - var button = UIButton.FromType (UIButtonType.RoundedRect); - button.SetTitle ("Login", UIControlState.Normal); - button.Frame = new CGRect (20, 100, 200, 44); - loginViewController.View.AddSubview (button); - - button.TouchUpInside += (sender, e) => { - Xamarin.Forms.Application.Current.MainPage = new ContentPage {Content = new Label {Text = "40911 Success"} }; - loginViewController.DismissViewController (true, null); + var button = UIButton.FromType(UIButtonType.RoundedRect); + button.SetTitle("Login", UIControlState.Normal); + button.Frame = new CGRect(20, 100, 200, 44); + loginViewController.View.AddSubview(button); + + button.TouchUpInside += (sender, e) => + { + Xamarin.Forms.Application.Current.MainPage = new ContentPage { Content = new Label { Text = "40911 Success" } }; + loginViewController.DismissViewController(true, null); }; - var window= UIApplication.SharedApplication.KeyWindow; + var window = UIApplication.SharedApplication.KeyWindow; var vc = window.RootViewController; while (vc.PresentedViewController != null) { vc = vc.PresentedViewController; } - vc.PresentViewController (loginViewController, true, null); + vc.PresentViewController(loginViewController, true, null); } #endregion } + [Register("KVOUISlider")] + public class KVOUISlider : UISlider + { + + public KVOUISlider() + { + ValueChanged += (s, e) => KVOValue = Value; + } + + float _kVOValue; + [Export("kvovalue")] + public float KVOValue + { + get + { + + return _kVOValue; + } + set + { + + WillChangeValue(nameof(KVOValue).ToLower()); + _kVOValue = Value = value; + DidChangeValue(nameof(KVOValue).ToLower()); + } + } + } + public class ColorConverter : IValueConverter { public object Convert(object value, Type targetType, object parameter, CultureInfo culture) diff --git a/Xamarin.Forms.ControlGallery.iOS/Xamarin.Forms.ControlGallery.iOS.csproj b/Xamarin.Forms.ControlGallery.iOS/Xamarin.Forms.ControlGallery.iOS.csproj index c1ef6f4c..9af0106a 100644 --- a/Xamarin.Forms.ControlGallery.iOS/Xamarin.Forms.ControlGallery.iOS.csproj +++ b/Xamarin.Forms.ControlGallery.iOS/Xamarin.Forms.ControlGallery.iOS.csproj @@ -24,7 +24,7 @@ 4 false i386, x86_64 - SdkOnly + None True Entitlements.plist @@ -268,4 +268,4 @@ - \ No newline at end of file + diff --git a/Xamarin.Forms.Controls/ControlGalleryPages/NativeBindingGalleryPage.cs b/Xamarin.Forms.Controls/ControlGalleryPages/NativeBindingGalleryPage.cs index 2941b4bb..f0f404c7 100644 --- a/Xamarin.Forms.Controls/ControlGalleryPages/NativeBindingGalleryPage.cs +++ b/Xamarin.Forms.Controls/ControlGalleryPages/NativeBindingGalleryPage.cs @@ -52,13 +52,14 @@ namespace Xamarin.Forms.Controls var label = new Label(); label.SetBinding(Label.TextProperty, "FormsLabel"); + var labelAge = new Label(); + labelAge.SetBinding(Label.TextProperty, nameof(vm.Age)); Layout.Children.Add(buttonNav); - Layout.Children.Add(label); - Layout.Children.Add(boxView); Layout.Children.Add(button); + Layout.Children.Add(labelAge); BindingContext = ViewModel = vm; ; @@ -74,35 +75,35 @@ namespace Xamarin.Forms.Controls public string FormsLabel { get { return _formsLabel; } - set { _formsLabel = value; OnPropertyChanged(); } + set { if (_formsLabel == value) return; _formsLabel = value; OnPropertyChanged(); } } string _nativeLabel; public string NativeLabel { get { return _nativeLabel; } - set { _nativeLabel = value; OnPropertyChanged(); } + set { if (_nativeLabel == value) return; _nativeLabel = value; OnPropertyChanged(); } } Color _nativeLabelColor; public Color NativeLabelColor { get { return _nativeLabelColor; } - set { _nativeLabelColor = value; OnPropertyChanged(); } + set { if (_nativeLabelColor == value) return; _nativeLabelColor = value; OnPropertyChanged(); } } int _age; public int Age { get { return _age; } - set { _age = value; OnPropertyChanged(); } + set { if (_age == value) return; _age = value; OnPropertyChanged(); } } bool _selected; public bool Selected { get { return _selected; } - set { _selected = value; OnPropertyChanged(); } + set { if (_selected == value) return; _selected = value; OnPropertyChanged(); } } } diff --git a/Xamarin.Forms.Core/NativeBindingHelpers.cs b/Xamarin.Forms.Core/NativeBindingHelpers.cs index 7e2db65b..e4cae7d9 100644 --- a/Xamarin.Forms.Core/NativeBindingHelpers.cs +++ b/Xamarin.Forms.Core/NativeBindingHelpers.cs @@ -43,8 +43,10 @@ namespace Xamarin.Forms propertyChanged.PropertyChanged += (sender, e) => { if (e.PropertyName != targetProperty) return; - SetValueFromNative(sender as TNativeView, targetProperty, bindableProperty); - }; + SetValueFromNative(sender as TNativeView, targetProperty, bindableProperty); + //we need to keep the listener around he same time we have the proxy + proxy.NativeINPCListener = propertyChanged; + }; if (binding != null && binding.Mode != BindingMode.OneWay) SetValueFromNative(target, targetProperty, bindableProperty); @@ -178,6 +180,7 @@ namespace Xamarin.Forms public WeakReference TargetReference { get; set; } public IList> BindingsBackpack { get; } = new List>(); public IList> ValuesBackpack { get; } = new List>(); + public INotifyPropertyChanged NativeINPCListener; public BindableObjectProxy(TNativeView target) { @@ -193,4 +196,4 @@ namespace Xamarin.Forms } } } -} \ No newline at end of file +} diff --git a/Xamarin.Forms.Platform.iOS/Extensions/UIViewExtensions.cs b/Xamarin.Forms.Platform.iOS/Extensions/UIViewExtensions.cs index eab88e81..4c3eed98 100644 --- a/Xamarin.Forms.Platform.iOS/Extensions/UIViewExtensions.cs +++ b/Xamarin.Forms.Platform.iOS/Extensions/UIViewExtensions.cs @@ -37,9 +37,31 @@ namespace Xamarin.Forms.Platform.iOS } NativeViewPropertyListener nativePropertyListener = null; - if (bindingBase.Mode == BindingMode.TwoWay) { + if (bindingBase.Mode == BindingMode.TwoWay) + { nativePropertyListener = new NativeViewPropertyListener(propertyName); - view.AddObserver(nativePropertyListener, propertyName, 0, IntPtr.Zero); + try + { + //TODO: We need to figure a way to map the value back to the real objectiveC property. + //the X.IOS camelcase property name won't work + var key = new Foundation.NSString(propertyName.ToLower()); + var valueKey = view.ValueForKey(key); + if (valueKey != null) + { + view.AddObserver(nativePropertyListener, key, Foundation.NSKeyValueObservingOptions.New, IntPtr.Zero); + } + } + catch (Foundation.MonoTouchException ex) + { + nativePropertyListener = null; + if (ex.Name == "NSUnknownKeyException") + { + System.Diagnostics.Debug.WriteLine("KVO not supported, try specify a UpdateSourceEventName instead."); + return; + } + throw ex; + } + } NativeBindingHelpers.SetBinding(view, propertyName, bindingBase, nativePropertyListener); diff --git a/Xamarin.Forms.Platform.iOS/NativeViewPropertyListener.cs b/Xamarin.Forms.Platform.iOS/NativeViewPropertyListener.cs index 2d872af2..4e6601ef 100644 --- a/Xamarin.Forms.Platform.iOS/NativeViewPropertyListener.cs +++ b/Xamarin.Forms.Platform.iOS/NativeViewPropertyListener.cs @@ -17,8 +17,10 @@ namespace Xamarin.Forms.Platform.iOS public override void ObserveValue(NSString keyPath, NSObject ofObject, NSDictionary change, IntPtr context) { - if (keyPath == TargetProperty) + if (keyPath.ToString().Equals(TargetProperty, StringComparison.InvariantCultureIgnoreCase)) PropertyChanged?.Invoke(ofObject, new PropertyChangedEventArgs(TargetProperty)); + else + base.ObserveValue(keyPath, ofObject, change, context); } } } -- cgit v1.2.3