summaryrefslogtreecommitdiff
path: root/Xamarin.Forms.Platform.iOS/Extensions/UIViewExtensions.cs
blob: 8248af11e9c76cdeca0bd40ca30a2d15719a487e (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
using System;
using System.Collections.Generic;
using System.Drawing;
using System.Linq;
using static System.String;
#if __MOBILE__
using UIKit;
namespace Xamarin.Forms.Platform.iOS
#else
using UIView = AppKit.NSView;

namespace Xamarin.Forms.Platform.MacOS
#endif
{
	public static class UIViewExtensions
	{
		public static IEnumerable<UIView> Descendants(this UIView self)
		{
			if (self.Subviews == null)
				return Enumerable.Empty<UIView>();
			return self.Subviews.Concat(self.Subviews.SelectMany(s => s.Descendants()));
		}

		public static SizeRequest GetSizeRequest(this UIView self, double widthConstraint, double heightConstraint,
			double minimumWidth = -1, double minimumHeight = -1)
		{
			CoreGraphics.CGSize s;
#if __MOBILE__
			s = self.SizeThatFits(new SizeF((float)widthConstraint, (float)heightConstraint));
#else
			var control = self as AppKit.NSControl;
			if (control != null)
				s = control.SizeThatFits(new CoreGraphics.CGSize(widthConstraint, heightConstraint));
			else
				s = self.FittingSize;
#endif
			var request = new Size(s.Width == float.PositiveInfinity ? double.PositiveInfinity : s.Width,
				s.Height == float.PositiveInfinity ? double.PositiveInfinity : s.Height);
			var minimum = new Size(minimumWidth < 0 ? request.Width : minimumWidth,
				minimumHeight < 0 ? request.Height : minimumHeight);
			return new SizeRequest(request, minimum);
		}

		public static void SetBinding(this UIView view, string propertyName, BindingBase bindingBase,
			string updateSourceEventName = null)
		{
			var binding = bindingBase as Binding;
			//This will allow setting bindings from Xaml by reusing the MarkupExtension
			updateSourceEventName = updateSourceEventName ?? binding?.UpdateSourceEventName;

			if (!IsNullOrEmpty(updateSourceEventName))
			{
				NativeBindingHelpers.SetBinding(view, propertyName, bindingBase, updateSourceEventName);
				return;
			}

			NativeViewPropertyListener nativePropertyListener = null;
			if (bindingBase.Mode == BindingMode.TwoWay)
			{
				nativePropertyListener = new NativeViewPropertyListener(propertyName);
				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);
					}
				}
#if __MOBILE__
				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;
				}
#else
				catch (Exception ex)
				{
					throw ex;
				}
#endif
			}

			NativeBindingHelpers.SetBinding(view, propertyName, bindingBase, nativePropertyListener);
		}

		public static void SetBinding(this UIView self, BindableProperty targetProperty, BindingBase binding)
		{
			NativeBindingHelpers.SetBinding(self, targetProperty, binding);
		}

		public static void SetValue(this UIView target, BindableProperty targetProperty, object value)
		{
			NativeBindingHelpers.SetValue(target, targetProperty, value);
		}

		public static void SetBindingContext(this UIView target, object bindingContext,
			Func<UIView, IEnumerable<UIView>> getChildren = null)
		{
			NativeBindingHelpers.SetBindingContext(target, bindingContext, getChildren);
		}

		internal static void TransferbindablePropertiesToWrapper(this UIView target, View wrapper)
		{
			NativeBindingHelpers.TransferBindablePropertiesToWrapper(target, wrapper);
		}

		internal static T FindDescendantView<T>(this UIView view) where T : UIView
		{
			var queue = new Queue<UIView>();
			queue.Enqueue(view);

			while (queue.Count > 0)
			{
				var descendantView = queue.Dequeue();

				var result = descendantView as T;
				if (result != null)
					return result;

				for (var i = 0; i < descendantView.Subviews.Length; i++)
					queue.Enqueue(descendantView.Subviews[i]);
			}

			return null;
		}

#if __MOBILE__
		internal static UIView FindFirstResponder(this UIView view)
		{
			if (view.IsFirstResponder)
				return view;

			foreach (var subView in view.Subviews)
			{
				var firstResponder = subView.FindFirstResponder();
				if (firstResponder != null)
					return firstResponder;
			}

			return null;
		}
#endif
	}
}