diff options
Diffstat (limited to 'Xamarin.Forms.Platform.iOS')
122 files changed, 18473 insertions, 0 deletions
diff --git a/Xamarin.Forms.Platform.iOS/CADisplayLinkTicker.cs b/Xamarin.Forms.Platform.iOS/CADisplayLinkTicker.cs new file mode 100644 index 00000000..29dd732a --- /dev/null +++ b/Xamarin.Forms.Platform.iOS/CADisplayLinkTicker.cs @@ -0,0 +1,73 @@ +using System; +using System.Collections.Concurrent; +using System.Threading; +#if __UNIFIED__ +using UIKit; +using CoreAnimation; +using Foundation; + +#else +using MonoTouch.UIKit; +using MonoTouch.CoreAnimation; +using MonoTouch.Foundation; +#endif + +namespace Xamarin.Forms.Platform.iOS +{ + internal class CADisplayLinkTicker : Ticker + { + readonly BlockingCollection<Action> _queue = new BlockingCollection<Action>(); + CADisplayLink _link; + + public CADisplayLinkTicker() + { + var thread = new Thread(StartThread); + thread.Start(); + } + + internal new static CADisplayLinkTicker Default + { + get { return Ticker.Default as CADisplayLinkTicker; } + } + + public void Invoke(Action action) + { + _queue.Add(action); + } + + protected override void DisableTimer() + { + if (_link != null) + { + _link.RemoveFromRunLoop(NSRunLoop.Current, NSRunLoop.NSRunLoopCommonModes); + _link.Dispose(); + } + _link = null; + } + + protected override void EnableTimer() + { + _link = CADisplayLink.Create(() => SendSignals()); + _link.AddToRunLoop(NSRunLoop.Current, NSRunLoop.NSRunLoopCommonModes); + } + + void StartThread() + { + while (true) + { + var action = _queue.Take(); + var previous = UIApplication.CheckForIllegalCrossThreadCalls; + UIApplication.CheckForIllegalCrossThreadCalls = false; + + CATransaction.Begin(); + action.Invoke(); + + while (_queue.TryTake(out action)) + action.Invoke(); + CATransaction.Commit(); + + UIApplication.CheckForIllegalCrossThreadCalls = previous; + } + } + } +}
\ No newline at end of file diff --git a/Xamarin.Forms.Platform.iOS/Cells/CellRenderer.cs b/Xamarin.Forms.Platform.iOS/Cells/CellRenderer.cs new file mode 100644 index 00000000..c01a7774 --- /dev/null +++ b/Xamarin.Forms.Platform.iOS/Cells/CellRenderer.cs @@ -0,0 +1,77 @@ +using System; +#if __UNIFIED__ +using UIKit; +using Foundation; + +#else +using MonoTouch.UIKit; +using MonoTouch.Foundation; +#endif + +namespace Xamarin.Forms.Platform.iOS +{ + public class CellRenderer : IRegisterable + { + static readonly BindableProperty RealCellProperty = BindableProperty.CreateAttached("RealCell", typeof(UITableViewCell), typeof(Cell), null); + + EventHandler _onForceUpdateSizeRequested; + + public virtual UITableViewCell GetCell(Cell item, UITableViewCell reusableCell, UITableView tv) + { + var tvc = reusableCell as CellTableViewCell ?? new CellTableViewCell(UITableViewCellStyle.Default, item.GetType().FullName); + + tvc.Cell = item; + + WireUpForceUpdateSizeRequested(item, tvc, tv); + + tvc.TextLabel.Text = item.ToString(); + + UpdateBackground(tvc, item); + return tvc; + } + + protected void UpdateBackground(UITableViewCell tableViewCell, Cell cell) + { + if (TemplatedItemsList<ItemsView<Cell>, Cell>.GetIsGroupHeader(cell)) + { + if (UIDevice.CurrentDevice.CheckSystemVersion(7, 0)) + tableViewCell.BackgroundColor = new UIColor(247f / 255f, 247f / 255f, 247f / 255f, 1); + } + else + { + // Must be set to a solid color or blending issues will occur + var bgColor = UIColor.White; + + var element = cell.RealParent as VisualElement; + if (element != null) + bgColor = element.BackgroundColor == Color.Default ? bgColor : element.BackgroundColor.ToUIColor(); + + tableViewCell.BackgroundColor = bgColor; + } + } + + protected void WireUpForceUpdateSizeRequested(Cell cell, UITableViewCell nativeCell, UITableView tableView) + { + cell.ForceUpdateSizeRequested -= _onForceUpdateSizeRequested; + + _onForceUpdateSizeRequested = delegate + { + var index = tableView.IndexPathForCell(nativeCell); + if (index != null) + tableView.ReloadRows(new[] { index }, UITableViewRowAnimation.None); + }; + + cell.ForceUpdateSizeRequested += _onForceUpdateSizeRequested; + } + + internal static UITableViewCell GetRealCell(BindableObject cell) + { + return (UITableViewCell)cell.GetValue(RealCellProperty); + } + + internal static void SetRealCell(BindableObject cell, UITableViewCell renderer) + { + cell.SetValue(RealCellProperty, renderer); + } + } +}
\ No newline at end of file diff --git a/Xamarin.Forms.Platform.iOS/Cells/CellTableViewCell.cs b/Xamarin.Forms.Platform.iOS/Cells/CellTableViewCell.cs new file mode 100644 index 00000000..a9265868 --- /dev/null +++ b/Xamarin.Forms.Platform.iOS/Cells/CellTableViewCell.cs @@ -0,0 +1,96 @@ +using System; +using System.ComponentModel; +#if __UNIFIED__ +using UIKit; + +#else +using MonoTouch.UIKit; +#endif + +namespace Xamarin.Forms.Platform.iOS +{ + public class CellTableViewCell : UITableViewCell, INativeElementView + { + Cell _cell; + + public Action<object, PropertyChangedEventArgs> PropertyChanged; + + public CellTableViewCell(UITableViewCellStyle style, string key) : base(style, key) + { + } + + public Cell Cell + { + get { return _cell; } + set + { + if (_cell == value) + return; + + if (_cell != null) + Device.BeginInvokeOnMainThread(_cell.SendDisappearing); + _cell = value; + if (_cell != null) + Device.BeginInvokeOnMainThread(_cell.SendAppearing); + } + } + + public Element Element => Cell; + + public void HandlePropertyChanged(object sender, PropertyChangedEventArgs e) + { + if (PropertyChanged != null) + PropertyChanged(this, e); + } + + internal static UITableViewCell GetNativeCell(UITableView tableView, Cell cell, bool recycleCells = false, string templateId = "") + { + var id = cell.GetType().FullName; + + var renderer = (CellRenderer)Registrar.Registered.GetHandler(cell.GetType()); + + ContextActionsCell contextCell = null; + UITableViewCell reusableCell = null; + if (cell.HasContextActions || recycleCells) + { + contextCell = (ContextActionsCell)tableView.DequeueReusableCell(ContextActionsCell.Key + templateId); + if (contextCell == null) + { + contextCell = new ContextActionsCell(templateId); + reusableCell = tableView.DequeueReusableCell(id); + } + else + { + contextCell.Close(); + reusableCell = contextCell.ContentCell; + + if (reusableCell.ReuseIdentifier.ToString() != id) + reusableCell = null; + } + } + else + reusableCell = tableView.DequeueReusableCell(id); + + var nativeCell = renderer.GetCell(cell, reusableCell, tableView); + + var cellWithContent = nativeCell; + + // Sometimes iOS for returns a dequeued cell whose Layer is hidden. + // This prevents it from showing up, so lets turn it back on! + if (cellWithContent.Layer.Hidden) + cellWithContent.Layer.Hidden = false; + + if (contextCell != null) + { + contextCell.Update(tableView, cell, nativeCell); + nativeCell = contextCell; + } + + // Because the layer was hidden we need to layout the cell by hand + if (cellWithContent != null) + cellWithContent.LayoutSubviews(); + + return nativeCell; + } + } +}
\ No newline at end of file diff --git a/Xamarin.Forms.Platform.iOS/Cells/EntryCellRenderer.cs b/Xamarin.Forms.Platform.iOS/Cells/EntryCellRenderer.cs new file mode 100644 index 00000000..1af49b4a --- /dev/null +++ b/Xamarin.Forms.Platform.iOS/Cells/EntryCellRenderer.cs @@ -0,0 +1,186 @@ +using System; +using System.ComponentModel; +using System.Drawing; +#if __UNIFIED__ +using UIKit; +#else +using MonoTouch.UIKit; +#endif +#if __UNIFIED__ +using RectangleF = CoreGraphics.CGRect; +using SizeF = CoreGraphics.CGSize; +using PointF = CoreGraphics.CGPoint; + +#else +using nfloat=System.Single; +using nint=System.Int32; +using nuint=System.UInt32; +#endif + +namespace Xamarin.Forms.Platform.iOS +{ + public class EntryCellRenderer : CellRenderer + { + static readonly Color DefaultTextColor = Color.Black; + + public override UITableViewCell GetCell(Cell item, UITableViewCell reusableCell, UITableView tv) + { + var entryCell = (EntryCell)item; + + var tvc = reusableCell as EntryCellTableViewCell; + if (tvc == null) + tvc = new EntryCellTableViewCell(item.GetType().FullName); + else + { + tvc.Cell.PropertyChanged -= OnCellPropertyChanged; + tvc.TextFieldTextChanged -= OnTextFieldTextChanged; + tvc.KeyboardDoneButtonPressed -= OnKeyBoardDoneButtonPressed; + } + + SetRealCell(item, tvc); + + tvc.Cell = item; + tvc.Cell.PropertyChanged += OnCellPropertyChanged; + tvc.TextFieldTextChanged += OnTextFieldTextChanged; + tvc.KeyboardDoneButtonPressed += OnKeyBoardDoneButtonPressed; + + WireUpForceUpdateSizeRequested(item, tvc, tv); + + UpdateBackground(tvc, entryCell); + UpdateLabel(tvc, entryCell); + UpdateText(tvc, entryCell); + UpdateKeyboard(tvc, entryCell); + UpdatePlaceholder(tvc, entryCell); + UpdateLabelColor(tvc, entryCell); + UpdateHorizontalTextAlignment(tvc, entryCell); + UpdateIsEnabled(tvc, entryCell); + + return tvc; + } + + static void OnCellPropertyChanged(object sender, PropertyChangedEventArgs e) + { + var entryCell = (EntryCell)sender; + var realCell = (EntryCellTableViewCell)GetRealCell(entryCell); + + if (e.PropertyName == EntryCell.LabelProperty.PropertyName) + UpdateLabel(realCell, entryCell); + else if (e.PropertyName == EntryCell.TextProperty.PropertyName) + UpdateText(realCell, entryCell); + else if (e.PropertyName == EntryCell.PlaceholderProperty.PropertyName) + UpdatePlaceholder(realCell, entryCell); + else if (e.PropertyName == EntryCell.KeyboardProperty.PropertyName) + UpdateKeyboard(realCell, entryCell); + else if (e.PropertyName == EntryCell.LabelColorProperty.PropertyName) + UpdateLabelColor(realCell, entryCell); + else if (e.PropertyName == EntryCell.HorizontalTextAlignmentProperty.PropertyName) + UpdateHorizontalTextAlignment(realCell, entryCell); + else if (e.PropertyName == Cell.IsEnabledProperty.PropertyName) + UpdateIsEnabled(realCell, entryCell); + } + + static void OnKeyBoardDoneButtonPressed(object sender, EventArgs e) + { + var cell = (EntryCellTableViewCell)sender; + var model = (EntryCell)cell.Cell; + + model.SendCompleted(); + } + + static void OnTextFieldTextChanged(object sender, EventArgs eventArgs) + { + var cell = (EntryCellTableViewCell)sender; + var model = (EntryCell)cell.Cell; + + model.Text = cell.TextField.Text; + } + + static void UpdateHorizontalTextAlignment(EntryCellTableViewCell cell, EntryCell entryCell) + { + cell.TextField.TextAlignment = entryCell.HorizontalTextAlignment.ToNativeTextAlignment(); + } + + static void UpdateIsEnabled(EntryCellTableViewCell cell, EntryCell entryCell) + { + cell.UserInteractionEnabled = entryCell.IsEnabled; + cell.TextLabel.Enabled = entryCell.IsEnabled; + cell.DetailTextLabel.Enabled = entryCell.IsEnabled; + cell.TextField.Enabled = entryCell.IsEnabled; + } + + static void UpdateKeyboard(EntryCellTableViewCell cell, EntryCell entryCell) + { + cell.TextField.ApplyKeyboard(entryCell.Keyboard); + } + + static void UpdateLabel(EntryCellTableViewCell cell, EntryCell entryCell) + { + cell.TextLabel.Text = entryCell.Label; + } + + static void UpdateLabelColor(EntryCellTableViewCell cell, EntryCell entryCell) + { + cell.TextLabel.TextColor = entryCell.LabelColor.ToUIColor(DefaultTextColor); + } + + static void UpdatePlaceholder(EntryCellTableViewCell cell, EntryCell entryCell) + { + cell.TextField.Placeholder = entryCell.Placeholder; + } + + static void UpdateText(EntryCellTableViewCell cell, EntryCell entryCell) + { + if (cell.TextField.Text == entryCell.Text) + return; + // double sets side effect on iOS, YAY + cell.TextField.Text = entryCell.Text; + } + + class EntryCellTableViewCell : CellTableViewCell + { + public EntryCellTableViewCell(string cellName) : base(UITableViewCellStyle.Value1, cellName) + { + TextField = new UITextField(new RectangleF(0, 0, 100, 30)) { BorderStyle = UITextBorderStyle.None }; + + TextField.EditingChanged += TextFieldOnEditingChanged; + TextField.ShouldReturn = OnShouldReturn; + + ContentView.AddSubview(TextField); + } + + public UITextField TextField { get; } + + public event EventHandler KeyboardDoneButtonPressed; + + public override void LayoutSubviews() + { + base.LayoutSubviews(); + + // simple algorithm to generally line up entries + var start = (nfloat)Math.Round(Math.Max(Frame.Width * 0.3, TextLabel.Frame.Right + 10)); + TextField.Frame = new RectangleF(start, (Frame.Height - 30) / 2, Frame.Width - TextLabel.Frame.Left - start, 30); + // Centers TextField Content (iOS6) + TextField.VerticalAlignment = UIControlContentVerticalAlignment.Center; + } + + public event EventHandler TextFieldTextChanged; + + bool OnShouldReturn(UITextField view) + { + var handler = KeyboardDoneButtonPressed; + if (handler != null) + handler(this, EventArgs.Empty); + + TextField.ResignFirstResponder(); + return true; + } + + void TextFieldOnEditingChanged(object sender, EventArgs eventArgs) + { + var handler = TextFieldTextChanged; + if (handler != null) + handler(this, EventArgs.Empty); + } + } + } +}
\ No newline at end of file diff --git a/Xamarin.Forms.Platform.iOS/Cells/ImageCellRenderer.cs b/Xamarin.Forms.Platform.iOS/Cells/ImageCellRenderer.cs new file mode 100644 index 00000000..ade59e82 --- /dev/null +++ b/Xamarin.Forms.Platform.iOS/Cells/ImageCellRenderer.cs @@ -0,0 +1,70 @@ +using System.ComponentModel; +using System.Threading.Tasks; +#if __UNIFIED__ +using UIKit; +using Foundation; + +#else +using MonoTouch.UIKit; +using MonoTouch.Foundation; +#endif + +namespace Xamarin.Forms.Platform.iOS +{ + public class ImageCellRenderer : TextCellRenderer + { + public override UITableViewCell GetCell(Cell item, UITableViewCell reusableCell, UITableView tv) + { + var result = (CellTableViewCell)base.GetCell(item, reusableCell, tv); + + var imageCell = (ImageCell)item; + + WireUpForceUpdateSizeRequested(item, result, tv); + + SetImage(imageCell, result); + + return result; + } + + protected override void HandlePropertyChanged(object sender, PropertyChangedEventArgs args) + { + var tvc = (CellTableViewCell)sender; + var imageCell = (ImageCell)tvc.Cell; + + base.HandlePropertyChanged(sender, args); + + if (args.PropertyName == ImageCell.ImageSourceProperty.PropertyName) + SetImage(imageCell, tvc); + } + + async void SetImage(ImageCell cell, CellTableViewCell target) + { + var source = cell.ImageSource; + + target.ImageView.Image = null; + + IImageSourceHandler handler; + + if (source != null && (handler = Registrar.Registered.GetHandler<IImageSourceHandler>(source.GetType())) != null) + { + UIImage uiimage; + try + { + uiimage = await handler.LoadImageAsync(source).ConfigureAwait(false); + } + catch (TaskCanceledException) + { + uiimage = null; + } + + NSRunLoop.Main.BeginInvokeOnMainThread(() => + { + target.ImageView.Image = uiimage; + target.SetNeedsLayout(); + }); + } + else + target.ImageView.Image = null; + } + } +}
\ No newline at end of file diff --git a/Xamarin.Forms.Platform.iOS/Cells/SwitchCellRenderer.cs b/Xamarin.Forms.Platform.iOS/Cells/SwitchCellRenderer.cs new file mode 100644 index 00000000..ed7ddc10 --- /dev/null +++ b/Xamarin.Forms.Platform.iOS/Cells/SwitchCellRenderer.cs @@ -0,0 +1,94 @@ +using System; +using System.ComponentModel; +using System.Drawing; +#if __UNIFIED__ +using UIKit; + +#else +using MonoTouch.UIKit; +#endif + +namespace Xamarin.Forms.Platform.iOS +{ + public class SwitchCellRenderer : CellRenderer + { + const string CellName = "Xamarin.SwitchCell"; + + public override UITableViewCell GetCell(Cell item, UITableViewCell reusableCell, UITableView tv) + { + var tvc = reusableCell as CellTableViewCell; + UISwitch uiSwitch = null; + if (tvc == null) + tvc = new CellTableViewCell(UITableViewCellStyle.Value1, CellName); + else + { + uiSwitch = tvc.AccessoryView as UISwitch; + tvc.Cell.PropertyChanged -= OnCellPropertyChanged; + } + + SetRealCell(item, tvc); + + if (uiSwitch == null) + { + uiSwitch = new UISwitch(new RectangleF()); + uiSwitch.ValueChanged += OnSwitchValueChanged; + tvc.AccessoryView = uiSwitch; + } + + var boolCell = (SwitchCell)item; + + tvc.Cell = item; + tvc.Cell.PropertyChanged += OnCellPropertyChanged; + tvc.AccessoryView = uiSwitch; + tvc.TextLabel.Text = boolCell.Text; + + uiSwitch.On = boolCell.On; + + WireUpForceUpdateSizeRequested(item, tvc, tv); + + UpdateBackground(tvc, item); + UpdateIsEnabled(tvc, boolCell); + + return tvc; + } + + void OnCellPropertyChanged(object sender, PropertyChangedEventArgs e) + { + var boolCell = (SwitchCell)sender; + var realCell = (CellTableViewCell)GetRealCell(boolCell); + + if (e.PropertyName == SwitchCell.OnProperty.PropertyName) + ((UISwitch)realCell.AccessoryView).SetState(boolCell.On, true); + else if (e.PropertyName == SwitchCell.TextProperty.PropertyName) + realCell.TextLabel.Text = boolCell.Text; + else if (e.PropertyName == Cell.IsEnabledProperty.PropertyName) + UpdateIsEnabled(realCell, boolCell); + } + + void OnSwitchValueChanged(object sender, EventArgs eventArgs) + { + var view = (UIView)sender; + var sw = (UISwitch)view; + + CellTableViewCell realCell = null; + while (view.Superview != null && realCell == null) + { + view = view.Superview; + realCell = view as CellTableViewCell; + } + + if (realCell != null) + ((SwitchCell)realCell.Cell).On = sw.On; + } + + void UpdateIsEnabled(CellTableViewCell cell, SwitchCell switchCell) + { + cell.UserInteractionEnabled = switchCell.IsEnabled; + cell.TextLabel.Enabled = switchCell.IsEnabled; + cell.DetailTextLabel.Enabled = switchCell.IsEnabled; + var uiSwitch = cell.AccessoryView as UISwitch; + if (uiSwitch != null) + uiSwitch.Enabled = switchCell.IsEnabled; + } + } +}
\ No newline at end of file diff --git a/Xamarin.Forms.Platform.iOS/Cells/TextCellRenderer.cs b/Xamarin.Forms.Platform.iOS/Cells/TextCellRenderer.cs new file mode 100644 index 00000000..41c43341 --- /dev/null +++ b/Xamarin.Forms.Platform.iOS/Cells/TextCellRenderer.cs @@ -0,0 +1,73 @@ +using System.ComponentModel; +#if __UNIFIED__ +using UIKit; + +#else +using MonoTouch.UIKit; +#endif + +namespace Xamarin.Forms.Platform.iOS +{ + public class TextCellRenderer : CellRenderer + { + static readonly Color DefaultDetailColor = new Color(.32, .4, .57); + static readonly Color DefaultTextColor = Color.Black; + + public override UITableViewCell GetCell(Cell item, UITableViewCell reusableCell, UITableView tv) + { + var textCell = (TextCell)item; + + var tvc = reusableCell as CellTableViewCell; + if (tvc == null) + tvc = new CellTableViewCell(UITableViewCellStyle.Subtitle, item.GetType().FullName); + else + tvc.Cell.PropertyChanged -= tvc.HandlePropertyChanged; + + tvc.Cell = textCell; + textCell.PropertyChanged += tvc.HandlePropertyChanged; + tvc.PropertyChanged = HandlePropertyChanged; + + tvc.TextLabel.Text = textCell.Text; + tvc.DetailTextLabel.Text = textCell.Detail; + tvc.TextLabel.TextColor = textCell.TextColor.ToUIColor(DefaultTextColor); + tvc.DetailTextLabel.TextColor = textCell.DetailColor.ToUIColor(DefaultDetailColor); + + WireUpForceUpdateSizeRequested(item, tvc, tv); + + UpdateIsEnabled(tvc, textCell); + + UpdateBackground(tvc, item); + + return tvc; + } + + protected virtual void HandlePropertyChanged(object sender, PropertyChangedEventArgs args) + { + var tvc = (CellTableViewCell)sender; + var textCell = (TextCell)tvc.Cell; + if (args.PropertyName == TextCell.TextProperty.PropertyName) + { + tvc.TextLabel.Text = ((TextCell)tvc.Cell).Text; + tvc.TextLabel.SizeToFit(); + } + else if (args.PropertyName == TextCell.DetailProperty.PropertyName) + { + tvc.DetailTextLabel.Text = ((TextCell)tvc.Cell).Detail; + tvc.DetailTextLabel.SizeToFit(); + } + else if (args.PropertyName == TextCell.TextColorProperty.PropertyName) + tvc.TextLabel.TextColor = textCell.TextColor.ToUIColor(DefaultTextColor); + else if (args.PropertyName == TextCell.DetailColorProperty.PropertyName) + tvc.DetailTextLabel.TextColor = textCell.DetailColor.ToUIColor(DefaultTextColor); + else if (args.PropertyName == Cell.IsEnabledProperty.PropertyName) + UpdateIsEnabled(tvc, textCell); + } + + static void UpdateIsEnabled(CellTableViewCell cell, TextCell entryCell) + { + cell.UserInteractionEnabled = entryCell.IsEnabled; + cell.TextLabel.Enabled = entryCell.IsEnabled; + cell.DetailTextLabel.Enabled = entryCell.IsEnabled; + } + } +}
\ No newline at end of file diff --git a/Xamarin.Forms.Platform.iOS/Cells/ViewCellRenderer.cs b/Xamarin.Forms.Platform.iOS/Cells/ViewCellRenderer.cs new file mode 100644 index 00000000..b1416295 --- /dev/null +++ b/Xamarin.Forms.Platform.iOS/Cells/ViewCellRenderer.cs @@ -0,0 +1,176 @@ +using System; +using System.ComponentModel; +using System.Diagnostics; +#if __UNIFIED__ +using UIKit; +#else +using MonoTouch.UIKit; +using System.Drawing; +#endif +#if __UNIFIED__ +using RectangleF = CoreGraphics.CGRect; +using SizeF = CoreGraphics.CGSize; +using PointF = CoreGraphics.CGPoint; + +#else +using nfloat=System.Single; +using nint=System.Int32; +using nuint=System.UInt32; +#endif + +namespace Xamarin.Forms.Platform.iOS +{ + public class ViewCellRenderer : CellRenderer + { + public override UITableViewCell GetCell(Cell item, UITableViewCell reusableCell, UITableView tv) + { + var viewCell = (ViewCell)item; + + var cell = reusableCell as ViewTableCell; + if (cell == null) + cell = new ViewTableCell(item.GetType().FullName); + else + cell.ViewCell.PropertyChanged -= ViewCellPropertyChanged; + + viewCell.PropertyChanged += ViewCellPropertyChanged; + cell.ViewCell = viewCell; + + WireUpForceUpdateSizeRequested(item, cell, tv); + + UpdateBackground(cell, item); + UpdateIsEnabled(cell, viewCell); + return cell; + } + + static void UpdateIsEnabled(ViewTableCell cell, ViewCell viewCell) + { + cell.UserInteractionEnabled = viewCell.IsEnabled; + cell.TextLabel.Enabled = viewCell.IsEnabled; + } + + void ViewCellPropertyChanged(object sender, PropertyChangedEventArgs e) + { + var viewCell = (ViewCell)sender; + var realCell = (ViewTableCell)GetRealCell(viewCell); + + if (e.PropertyName == Cell.IsEnabledProperty.PropertyName) + UpdateIsEnabled(realCell, viewCell); + } + + internal class ViewTableCell : UITableViewCell, INativeElementView + { + WeakReference<IVisualElementRenderer> _rendererRef; + + ViewCell _viewCell; + + public ViewTableCell(string key) : base(UITableViewCellStyle.Default, key) + { + } + + public ViewCell ViewCell + { + get { return _viewCell; } + set + { + if (_viewCell == value) + return; + UpdateCell(value); + } + } + + Element INativeElementView.Element + { + get { return ViewCell; } + } + + public override void LayoutSubviews() + { + //This sets the content views frame. + base.LayoutSubviews(); + + var contentFrame = ContentView.Frame; + + Layout.LayoutChildIntoBoundingRegion(ViewCell.View, contentFrame.ToRectangle()); + + if (_rendererRef == null) + return; + + IVisualElementRenderer renderer; + if (_rendererRef.TryGetTarget(out renderer)) + renderer.NativeView.Frame = contentFrame; + } + + public override SizeF SizeThatFits(SizeF size) + { + IVisualElementRenderer renderer; + if (!_rendererRef.TryGetTarget(out renderer)) + return base.SizeThatFits(size); + + double width = size.Width; + var height = size.Height > 0 ? size.Height : double.PositiveInfinity; + var result = renderer.Element.Measure(width, height); + + // make sure to add in the separator + return new SizeF(size.Width, (float)result.Request.Height + 1f / UIScreen.MainScreen.Scale); + } + + protected override void Dispose(bool disposing) + { + if (disposing) + { + IVisualElementRenderer renderer; + if (_rendererRef != null && _rendererRef.TryGetTarget(out renderer) && renderer.Element != null) + { + var platform = renderer.Element.Platform as Platform; + if (platform != null) + platform.DisposeModelAndChildrenRenderers(renderer.Element); + + _rendererRef = null; + } + } + + base.Dispose(disposing); + } + + IVisualElementRenderer GetNewRenderer() + { + var newRenderer = Platform.CreateRenderer(_viewCell.View); + _rendererRef = new WeakReference<IVisualElementRenderer>(newRenderer); + ContentView.AddSubview(newRenderer.NativeView); + return newRenderer; + } + + void UpdateCell(ViewCell cell) + { + if (_viewCell != null) + Device.BeginInvokeOnMainThread(_viewCell.SendDisappearing); + + _viewCell = cell; + Device.BeginInvokeOnMainThread(_viewCell.SendAppearing); + + IVisualElementRenderer renderer; + if (_rendererRef == null || !_rendererRef.TryGetTarget(out renderer)) + renderer = GetNewRenderer(); + else + { + if (renderer.Element != null && renderer == Platform.GetRenderer(renderer.Element)) + renderer.Element.ClearValue(Platform.RendererProperty); + + var type = Registrar.Registered.GetHandlerType(_viewCell.View.GetType()); + if (renderer.GetType() == type || (renderer is Platform.DefaultRenderer && type == null)) + renderer.SetElement(_viewCell.View); + else + { + //when cells are getting reused the element could be already set to another cell + //so we should dispose based on the renderer and not the renderer.Element + var platform = renderer.Element.Platform as Platform; + platform.DisposeRendererAndChildren(renderer); + renderer = GetNewRenderer(); + } + } + + Platform.SetRenderer(_viewCell.View, renderer); + } + } + } +}
\ No newline at end of file diff --git a/Xamarin.Forms.Platform.iOS/ContextActionCell.cs b/Xamarin.Forms.Platform.iOS/ContextActionCell.cs new file mode 100644 index 00000000..a2409c62 --- /dev/null +++ b/Xamarin.Forms.Platform.iOS/ContextActionCell.cs @@ -0,0 +1,715 @@ +using System; +using System.Collections.Specialized; +using System.ComponentModel; +using System.Collections.Generic; +using System.Drawing; +using Xamarin.Forms.Platform.iOS.Resources; +#if __UNIFIED__ +using UIKit; +using Foundation; +#else +using MonoTouch.UIKit; +using MonoTouch.Foundation; +#endif +#if __UNIFIED__ +using RectangleF = CoreGraphics.CGRect; +using SizeF = CoreGraphics.CGSize; +using PointF = CoreGraphics.CGPoint; + +#else +using nfloat=System.Single; +using nint=System.Int32; +#endif + +namespace Xamarin.Forms.Platform.iOS +{ + internal class ContextActionsCell : UITableViewCell, INativeElementView + { + public const string Key = "ContextActionsCell"; + + static readonly UIImage DestructiveBackground; + static readonly UIImage NormalBackground; + readonly List<UIButton> _buttons = new List<UIButton>(); + readonly List<MenuItem> _menuItems = new List<MenuItem>(); + + Cell _cell; + UIButton _moreButton; + UIScrollView _scroller; + UITableView _tableView; + + static ContextActionsCell() + { + var rect = new RectangleF(0, 0, 1, 1); + var size = rect.Size; + + UIGraphics.BeginImageContext(size); + var context = UIGraphics.GetCurrentContext(); + context.SetFillColor(1, 0, 0, 1); + context.FillRect(rect); + DestructiveBackground = UIGraphics.GetImageFromCurrentImageContext(); + + context.SetFillColor(UIColor.LightGray.ToColor().ToCGColor()); + context.FillRect(rect); + + NormalBackground = UIGraphics.GetImageFromCurrentImageContext(); + + context.Dispose(); + } + + public ContextActionsCell() : base(UITableViewCellStyle.Default, Key) + { + } + + public ContextActionsCell(string templateId) : base(UITableViewCellStyle.Default, Key + templateId) + { + } + + public UITableViewCell ContentCell { get; private set; } + + public bool IsOpen + { + get { return ScrollDelegate.IsOpen; } + } + + ContextScrollViewDelegate ScrollDelegate + { + get { return (ContextScrollViewDelegate)_scroller.Delegate; } + } + + Element INativeElementView.Element + { + get + { + var boxedCell = ContentCell as INativeElementView; + if (boxedCell == null) + { + throw new InvalidOperationException($"Implement {nameof(INativeElementView)} on cell renderer: {ContentCell.GetType().AssemblyQualifiedName}"); + } + + return boxedCell.Element; + } + } + + public void Close() + { + _scroller.ContentOffset = new PointF(0, 0); + } + + public override void LayoutSubviews() + { + base.LayoutSubviews(); + + if (_scroller == null || (_scroller != null && _scroller.Frame == Bounds)) + return; + + Update(_tableView, _cell, ContentCell); + + _scroller.Frame = Bounds; + ContentCell.Frame = Bounds; + + if (ContentCell is ViewCellRenderer.ViewTableCell && ContentCell.Subviews.Length > 0 && Math.Abs(ContentCell.Subviews[0].Frame.Height - Bounds.Height) > 1) + { + // Something goes weird inside iOS where LayoutSubviews wont get called when updating the bounds if the user + // forces us to flip flop between a ContextActionCell and a normal cell in the middle of actually displaying the cell + // so here we are going to hack it a forced update. Leave room for 1px of play because the border is 1 or .5px and must + // be accounted for. + // + // Fixes https://bugzilla.xamarin.com/show_bug.cgi?id=39450 + ContentCell.LayoutSubviews(); + } + } + + public void PrepareForDeselect() + { + ScrollDelegate.PrepareForDeselect(_scroller); + } + + public override SizeF SizeThatFits(SizeF size) + { + return ContentCell.SizeThatFits(size); + } + + public void Update(UITableView tableView, Cell cell, UITableViewCell nativeCell) + { + var parentListView = cell.RealParent as ListView; + var recycling = parentListView != null && parentListView.CachingStrategy == ListViewCachingStrategy.RecycleElement; + if (_cell != cell && recycling) + { + if (_cell != null) + ((INotifyCollectionChanged)_cell.ContextActions).CollectionChanged -= OnContextItemsChanged; + + ((INotifyCollectionChanged)cell.ContextActions).CollectionChanged += OnContextItemsChanged; + } + + var height = Frame.Height; + var width = tableView.Frame.Width; + + nativeCell.Frame = new RectangleF(0, 0, width, height); + nativeCell.SetNeedsLayout(); + + var handler = new PropertyChangedEventHandler(OnMenuItemPropertyChanged); + + _tableView = tableView; + SetupSelection(tableView); + + if (_cell != null) + { + if (!recycling) + _cell.PropertyChanged -= OnCellPropertyChanged; + if (_menuItems.Count > 0) + { + if (!recycling) + ((INotifyCollectionChanged)_cell.ContextActions).CollectionChanged -= OnContextItemsChanged; + + foreach (var item in _menuItems) + item.PropertyChanged -= handler; + } + + _menuItems.Clear(); + } + + _menuItems.AddRange(cell.ContextActions); + + _cell = cell; + if (!recycling) + { + cell.PropertyChanged += OnCellPropertyChanged; + ((INotifyCollectionChanged)_cell.ContextActions).CollectionChanged += OnContextItemsChanged; + } + + var isOpen = false; + + if (_scroller == null) + { + _scroller = new UIScrollView(new RectangleF(0, 0, width, height)); + _scroller.ScrollsToTop = false; + _scroller.ShowsHorizontalScrollIndicator = false; + + if (Forms.IsiOS8OrNewer) + _scroller.PreservesSuperviewLayoutMargins = true; + + ContentView.AddSubview(_scroller); + } + else + { + _scroller.Frame = new RectangleF(0, 0, width, height); + isOpen = ScrollDelegate.IsOpen; + + for (var i = 0; i < _buttons.Count; i++) + { + var b = _buttons[i]; + b.RemoveFromSuperview(); + b.Dispose(); + } + + _buttons.Clear(); + + ScrollDelegate.Unhook(_scroller); + ScrollDelegate.Dispose(); + } + + if (ContentCell != nativeCell) + { + if (ContentCell != null) + { + ContentCell.RemoveFromSuperview(); + ContentCell = null; + } + + ContentCell = nativeCell; + + //Hack: if we have a ImageCell the insets are slightly different, + //the inset numbers user below were taken using the Reveal app from the default cells + if ((ContentCell as CellTableViewCell)?.Cell is ImageCell) + { + nfloat imageCellInsetLeft = 57; + nfloat imageCellInsetRight = 0; + if (UIDevice.CurrentDevice.UserInterfaceIdiom == UIUserInterfaceIdiom.Pad) + { + imageCellInsetLeft = 89; + imageCellInsetRight = imageCellInsetLeft / 2; + } + SeparatorInset = new UIEdgeInsets(0, imageCellInsetLeft, 0, imageCellInsetRight); + } + + _scroller.AddSubview(nativeCell); + } + + SetupButtons(width, height); + + UIView container = null; + + var totalWidth = width; + for (var i = _buttons.Count - 1; i >= 0; i--) + { + var b = _buttons[i]; + totalWidth += b.Frame.Width; + + if (UIDevice.CurrentDevice.CheckSystemVersion(8, 0)) + _scroller.AddSubview(b); + else + { + if (container == null) + { + container = new iOS7ButtonContainer(b.Frame.Width); + _scroller.InsertSubview(container, 0); + } + + container.AddSubview(b); + } + } + + _scroller.Delegate = new ContextScrollViewDelegate(container, _buttons, isOpen); + _scroller.ContentSize = new SizeF(totalWidth, height); + + if (isOpen) + _scroller.SetContentOffset(new PointF(ScrollDelegate.ButtonsWidth, 0), false); + else + _scroller.SetContentOffset(new PointF(0, 0), false); + } + + protected override void Dispose(bool disposing) + { + if (disposing) + { + if (_scroller != null) + { + _scroller.Dispose(); + _scroller = null; + } + + _tableView = null; + + if (_moreButton != null) + { + _moreButton.Dispose(); + _moreButton = null; + } + + for (var i = 0; i < _buttons.Count; i++) + _buttons[i].Dispose(); + + _buttons.Clear(); + _menuItems.Clear(); + + if (_cell != null) + { + if (_cell.HasContextActions) + ((INotifyCollectionChanged)_cell.ContextActions).CollectionChanged -= OnContextItemsChanged; + _cell = null; + } + } + + base.Dispose(disposing); + } + + void ActivateMore() + { + var displayed = new HashSet<nint>(); + for (var i = 0; i < _buttons.Count; i++) + { + var tag = _buttons[i].Tag; + if (tag >= 0) + displayed.Add(tag); + } + + var frame = _moreButton.Frame; + if (!Forms.IsiOS8OrNewer) + { + var container = _moreButton.Superview; + frame = new RectangleF(container.Frame.X, 0, frame.Width, frame.Height); + } + + var x = frame.X - _scroller.ContentOffset.X; + + var path = _tableView.IndexPathForCell(this); + var rowPosition = _tableView.RectForRowAtIndexPath(path); + var sourceRect = new RectangleF(x, rowPosition.Y, rowPosition.Width, rowPosition.Height); + + if (UIDevice.CurrentDevice.CheckSystemVersion(8, 0)) + { + var actionSheet = new MoreActionSheetController(); + + for (var i = 0; i < _cell.ContextActions.Count; i++) + { + if (displayed.Contains(i)) + continue; + + var item = _cell.ContextActions[i]; + var weakItem = new WeakReference<MenuItem>(item); + var action = UIAlertAction.Create(item.Text, UIAlertActionStyle.Default, a => + { + _scroller.SetContentOffset(new PointF(0, 0), true); + MenuItem mi; + if (weakItem.TryGetTarget(out mi)) + mi.Activate(); + }); + actionSheet.AddAction(action); + } + + var controller = GetController(); + if (controller == null) + throw new InvalidOperationException("No UIViewController found to present."); + + if (UIDevice.CurrentDevice.UserInterfaceIdiom == UIUserInterfaceIdiom.Phone) + { + var cancel = UIAlertAction.Create(StringResources.Cancel, UIAlertActionStyle.Cancel, null); + actionSheet.AddAction(cancel); + } + else + { + actionSheet.PopoverPresentationController.SourceView = _tableView; + actionSheet.PopoverPresentationController.SourceRect = sourceRect; + } + + controller.PresentViewController(actionSheet, true, null); + } + else + { + var d = new MoreActionSheetDelegate { Scroller = _scroller, Items = new List<MenuItem>() }; + + var actionSheet = new UIActionSheet(null, d); + + for (var i = 0; i < _cell.ContextActions.Count; i++) + { + if (displayed.Contains(i)) + continue; + + var item = _cell.ContextActions[i]; + d.Items.Add(item); + actionSheet.AddButton(item.Text); + } + + if (UIDevice.CurrentDevice.UserInterfaceIdiom == UIUserInterfaceIdiom.Phone) + { + var index = actionSheet.AddButton(StringResources.Cancel); + actionSheet.CancelButtonIndex = index; + } + + actionSheet.ShowFrom(sourceRect, _tableView, true); + } + } + + void CullButtons(nfloat acceptableTotalWidth, ref bool needMoreButton, ref nfloat largestButtonWidth) + { + while (largestButtonWidth * (_buttons.Count + (needMoreButton ? 1 : 0)) > acceptableTotalWidth && _buttons.Count > 1) + { + needMoreButton = true; + + var button = _buttons[_buttons.Count - 1]; + _buttons.RemoveAt(_buttons.Count - 1); + + if (largestButtonWidth == button.Frame.Width) + largestButtonWidth = GetLargestWidth(); + } + + if (needMoreButton && _cell.ContextActions.Count - _buttons.Count == 1) + _buttons.RemoveAt(_buttons.Count - 1); + } + + UIButton GetButton(MenuItem item) + { + var button = new UIButton(new RectangleF(0, 0, 1, 1)); + + if (!item.IsDestructive) + button.SetBackgroundImage(NormalBackground, UIControlState.Normal); + else + button.SetBackgroundImage(DestructiveBackground, UIControlState.Normal); + + button.SetTitle(item.Text, UIControlState.Normal); + button.TitleEdgeInsets = new UIEdgeInsets(0, 15, 0, 15); + + button.Enabled = item.IsEnabled; + + return button; + } + + UIViewController GetController() + { + Element e = _cell; + while (e.RealParent != null) + { + var renderer = Platform.GetRenderer((VisualElement)e.RealParent); + if (renderer.ViewController != null) + return renderer.ViewController; + + e = e.RealParent; + } + + return null; + } + + nfloat GetLargestWidth() + { + nfloat largestWidth = 0; + for (var i = 0; i < _buttons.Count; i++) + { + var frame = _buttons[i].Frame; + if (frame.Width > largestWidth) + largestWidth = frame.Width; + } + + return largestWidth; + } + + void OnButtonActivated(object sender, EventArgs e) + { + var button = (UIButton)sender; + if (button.Tag == -1) + ActivateMore(); + else + { + _scroller.SetContentOffset(new PointF(0, 0), true); + _cell.ContextActions[(int)button.Tag].Activate(); + } + } + + void OnCellPropertyChanged(object sender, PropertyChangedEventArgs e) + { + if (e.PropertyName == "HasContextActions") + { + var parentListView = _cell.RealParent as ListView; + var recycling = parentListView != null && parentListView.CachingStrategy == ListViewCachingStrategy.RecycleElement; + if (!recycling) + ReloadRow(); + } + } + + void OnContextItemsChanged(object sender, NotifyCollectionChangedEventArgs e) + { + var parentListView = _cell.RealParent as ListView; + var recycling = parentListView != null && parentListView.CachingStrategy == ListViewCachingStrategy.RecycleElement; + if (recycling) + Update(_tableView, _cell, ContentCell); + else + ReloadRow(); + // TODO: Perhaps make this nicer if it's open while adding + } + + void OnMenuItemPropertyChanged(object sender, PropertyChangedEventArgs e) + { + var parentListView = _cell.RealParent as ListView; + var recycling = parentListView != null && parentListView.CachingStrategy == ListViewCachingStrategy.RecycleElement; + if (recycling) + Update(_tableView, _cell, ContentCell); + else + ReloadRow(); + } + + void ReloadRow() + { + if (_scroller.ContentOffset.X > 0) + { + ((ContextScrollViewDelegate)_scroller.Delegate).ClosedCallback = () => + { + ReloadRowCore(); + ((ContextScrollViewDelegate)_scroller.Delegate).ClosedCallback = null; + }; + + _scroller.SetContentOffset(new PointF(0, 0), true); + } + else + ReloadRowCore(); + } + + void ReloadRowCore() + { + if (_cell.RealParent == null) + return; + + var path = _cell.GetIndexPath(); + + var selected = path.Equals(_tableView.IndexPathForSelectedRow); + + _tableView.ReloadRows(new[] { path }, UITableViewRowAnimation.None); + + if (selected) + { + _tableView.SelectRow(path, false, UITableViewScrollPosition.None); + _tableView.Source.RowSelected(_tableView, path); + } + } + + UIView SetupButtons(nfloat width, nfloat height) + { + MenuItem destructive = null; + nfloat largestWidth = 0, acceptableSize = width * 0.80f; + + for (var i = 0; i < _cell.ContextActions.Count; i++) + { + var item = _cell.ContextActions[i]; + + if (_buttons.Count == 3) + { + if (destructive != null) + break; + if (!item.IsDestructive) + continue; + + _buttons.RemoveAt(_buttons.Count - 1); + } + + if (item.IsDestructive) + destructive = item; + + var button = GetButton(item); + button.Tag = i; + var buttonWidth = button.TitleLabel.SizeThatFits(new SizeF(width, height)).Width + 30; + if (buttonWidth > largestWidth) + largestWidth = buttonWidth; + + if (destructive == item) + _buttons.Insert(0, button); + else + _buttons.Add(button); + } + + var needMore = _cell.ContextActions.Count > _buttons.Count; + + if (_cell.ContextActions.Count > 2) + CullButtons(acceptableSize, ref needMore, ref largestWidth); + + var resize = false; + if (needMore) + { + if (largestWidth * 2 > acceptableSize) + { + largestWidth = acceptableSize / 2; + resize = true; + } + + var button = new UIButton(new RectangleF(0, 0, largestWidth, height)); + button.SetBackgroundImage(NormalBackground, UIControlState.Normal); + button.TitleEdgeInsets = new UIEdgeInsets(0, 15, 0, 15); + button.SetTitle(StringResources.More, UIControlState.Normal); + + var moreWidth = button.TitleLabel.SizeThatFits(new SizeF(width, height)).Width + 30; + if (moreWidth > largestWidth) + { + largestWidth = moreWidth; + CullButtons(acceptableSize, ref needMore, ref largestWidth); + + if (largestWidth * 2 > acceptableSize) + { + largestWidth = acceptableSize / 2; + resize = true; + } + } + + button.Tag = -1; + button.TouchUpInside += OnButtonActivated; + if (resize) + button.TitleLabel.AdjustsFontSizeToFitWidth = true; + + _moreButton = button; + _buttons.Add(button); + } + + var handler = new PropertyChangedEventHandler(OnMenuItemPropertyChanged); + var totalWidth = _buttons.Count * largestWidth; + for (var n = 0; n < _buttons.Count; n++) + { + var b = _buttons[n]; + + if (b.Tag >= 0) + { + var item = _cell.ContextActions[(int)b.Tag]; + item.PropertyChanged += handler; + } + + var offset = (n + 1) * largestWidth; + + var x = width - offset; + if (UIDevice.CurrentDevice.CheckSystemVersion(8, 0)) + x += totalWidth; + + b.Frame = new RectangleF(x, 0, largestWidth, height); + if (resize) + b.TitleLabel.AdjustsFontSizeToFitWidth = true; + + b.SetNeedsLayout(); + + if (b != _moreButton) + b.TouchUpInside += OnButtonActivated; + } + + return null; + } + + void SetupSelection(UITableView table) + { + for (var i = 0; i < table.GestureRecognizers.Length; i++) + { + var r = table.GestureRecognizers[i] as SelectGestureRecognizer; + if (r != null) + return; + } + + _tableView.AddGestureRecognizer(new SelectGestureRecognizer()); + } + + class SelectGestureRecognizer : UITapGestureRecognizer + { + NSIndexPath _lastPath; + + public SelectGestureRecognizer() : base(Tapped) + { + ShouldReceiveTouch = (recognizer, touch) => + { + var table = (UITableView)View; + var pos = touch.LocationInView(table); + + _lastPath = table.IndexPathForRowAtPoint(pos); + if (_lastPath == null) + return false; + + var cell = table.CellAt(_lastPath) as ContextActionsCell; + + return cell != null; + }; + } + + static void Tapped(UIGestureRecognizer recognizer) + { + var selector = (SelectGestureRecognizer)recognizer; + + var table = (UITableView)recognizer.View; + + if (!selector._lastPath.Equals(table.IndexPathForSelectedRow)) + table.SelectRow(selector._lastPath, false, UITableViewScrollPosition.None); + table.Source.RowSelected(table, selector._lastPath); + } + } + + class MoreActionSheetController : UIAlertController + { + public override UIAlertControllerStyle PreferredStyle + { + get { return UIAlertControllerStyle.ActionSheet; } + } + + public override void WillRotate(UIInterfaceOrientation toInterfaceOrientation, double duration) + { + DismissViewController(false, null); + } + } + + class MoreActionSheetDelegate : UIActionSheetDelegate + { + public List<MenuItem> Items; + public UIScrollView Scroller; + + public override void Clicked(UIActionSheet actionSheet, nint buttonIndex) + { + if (buttonIndex == Items.Count) + return; // Cancel button + + Scroller.SetContentOffset(new PointF(0, 0), true); + + // do not activate a -1 index when dismissing by clicking outside the popover + if (buttonIndex >= 0) + Items[(int)buttonIndex].Activate(); + } + } + } +}
\ No newline at end of file diff --git a/Xamarin.Forms.Platform.iOS/ContextScrollViewDelegate.cs b/Xamarin.Forms.Platform.iOS/ContextScrollViewDelegate.cs new file mode 100644 index 00000000..47ce0714 --- /dev/null +++ b/Xamarin.Forms.Platform.iOS/ContextScrollViewDelegate.cs @@ -0,0 +1,298 @@ +using System; +using System.Drawing; +using System.Collections.Generic; +#if __UNIFIED__ +using UIKit; +using Foundation; +#else +using MonoTouch.UIKit; +using MonoTouch.Foundation; +#endif +#if __UNIFIED__ +using NSAction = System.Action; +using RectangleF = CoreGraphics.CGRect; +using SizeF = CoreGraphics.CGSize; +using PointF = CoreGraphics.CGPoint; + +#else +using nfloat=System.Single; +using nint=System.Int32; +#endif + +namespace Xamarin.Forms.Platform.iOS +{ + internal class iOS7ButtonContainer : UIView + { + readonly nfloat _buttonWidth; + + public iOS7ButtonContainer(nfloat buttonWidth) : base(new RectangleF(0, 0, 0, 0)) + { + _buttonWidth = buttonWidth; + ClipsToBounds = true; + } + + public override void LayoutSubviews() + { + var width = Frame.Width; + nfloat takenSpace = 0; + + for (var i = 0; i < Subviews.Length; i++) + { + var view = Subviews[i]; + + var pos = Subviews.Length - i; + var x = width - _buttonWidth * pos; + view.Frame = new RectangleF(x, 0, view.Frame.Width, view.Frame.Height); + + takenSpace += view.Frame.Width; + } + } + } + + internal class ContextScrollViewDelegate : UIScrollViewDelegate + { + readonly nfloat _finalButtonSize; + UIView _backgroundView; + List<UIButton> _buttons; + UITapGestureRecognizer _closer; + UIView _container; + GlobalCloseContextGestureRecognizer _globalCloser; + + bool _isDisposed; + + UITableView _table; + + public ContextScrollViewDelegate(UIView container, List<UIButton> buttons, bool isOpen) + { + IsOpen = isOpen; + _container = container; + _buttons = buttons; + + for (var i = 0; i < buttons.Count; i++) + { + var b = buttons[i]; + b.Hidden = !isOpen; + + ButtonsWidth += b.Frame.Width; + _finalButtonSize = b.Frame.Width; + } + } + + public nfloat ButtonsWidth { get; } + + public Action ClosedCallback { get; set; } + + public bool IsOpen { get; private set; } + + public override void DraggingStarted(UIScrollView scrollView) + { + if (!IsOpen) + SetButtonsShowing(true); + + var cell = GetContextCell(scrollView); + if (!cell.Selected) + return; + + if (!IsOpen) + RemoveHighlight(scrollView); + } + + public void PrepareForDeselect(UIScrollView scrollView) + { + RestoreHighlight(scrollView); + } + + public override void Scrolled(UIScrollView scrollView) + { + var width = _finalButtonSize; + var count = _buttons.Count; + + if (!UIDevice.CurrentDevice.CheckSystemVersion(8, 0)) + _container.Frame = new RectangleF(scrollView.Frame.Width, 0, scrollView.ContentOffset.X, scrollView.Frame.Height); + else + { + var ioffset = scrollView.ContentOffset.X / (float)count; + + if (ioffset > width) + width = ioffset + 1; + + for (var i = count - 1; i >= 0; i--) + { + var b = _buttons[i]; + var rect = b.Frame; + b.Frame = new RectangleF(scrollView.Frame.Width + (count - (i + 1)) * ioffset, 0, width, rect.Height); + } + } + + if (scrollView.ContentOffset.X == 0) + { + IsOpen = false; + SetButtonsShowing(false); + RestoreHighlight(scrollView); + + ClearCloserRecognizer(scrollView); + + if (ClosedCallback != null) + ClosedCallback(); + } + } + + public void Unhook(UIScrollView scrollView) + { + RestoreHighlight(scrollView); + ClearCloserRecognizer(scrollView); + } + + public override void WillEndDragging(UIScrollView scrollView, PointF velocity, ref PointF targetContentOffset) + { + var width = ButtonsWidth; + var x = targetContentOffset.X; + var parentThreshold = scrollView.Frame.Width * .4f; + var contentThreshold = width * .8f; + + if (x >= parentThreshold || x >= contentThreshold) + { + IsOpen = true; + targetContentOffset = new PointF(width, 0); + RemoveHighlight(scrollView); + + if (_globalCloser == null) + { + UIView view = scrollView; + while (view.Superview != null) + { + view = view.Superview; + + NSAction close = () => + { + if (UIDevice.CurrentDevice.CheckSystemVersion(8, 0)) + RestoreHighlight(scrollView); + + IsOpen = false; + scrollView.SetContentOffset(new PointF(0, 0), true); + + ClearCloserRecognizer(scrollView); + }; + + var table = view as UITableView; + if (table != null) + { + _table = table; + _globalCloser = new GlobalCloseContextGestureRecognizer(scrollView, _buttons, close); + _globalCloser.ShouldRecognizeSimultaneously = (recognizer, r) => r == _table.PanGestureRecognizer; + table.AddGestureRecognizer(_globalCloser); + + _closer = new UITapGestureRecognizer(close); + var cell = GetContextCell(scrollView); + cell.ContentCell.AddGestureRecognizer(_closer); + } + } + } + } + else + { + ClearCloserRecognizer(scrollView); + + IsOpen = false; + targetContentOffset = new PointF(0, 0); + + if (UIDevice.CurrentDevice.CheckSystemVersion(8, 0)) + RestoreHighlight(scrollView); + } + } + + protected override void Dispose(bool disposing) + { + if (_isDisposed) + return; + + _isDisposed = true; + + if (disposing) + { + ClosedCallback = null; + + _table = null; + _backgroundView = null; + _container = null; + + _buttons = null; + } + + base.Dispose(disposing); + } + + void ClearCloserRecognizer(UIScrollView scrollView) + { + if (_globalCloser == null) + return; + + var cell = GetContextCell(scrollView); + cell.ContentCell.RemoveGestureRecognizer(_closer); + _closer.Dispose(); + _closer = null; + + _table.RemoveGestureRecognizer(_globalCloser); + _table = null; + _globalCloser.Dispose(); + _globalCloser = null; + } + + ContextActionsCell GetContextCell(UIScrollView scrollView) + { + var view = scrollView.Superview.Superview; + var cell = view as ContextActionsCell; + while (view.Superview != null) + { + cell = view as ContextActionsCell; + if (cell != null) + break; + + view = view.Superview; + } + + return cell; + } + + void RemoveHighlight(UIScrollView scrollView) + { + var subviews = scrollView.Superview.Superview.Subviews; + + var count = 0; + for (var i = 0; i < subviews.Length; i++) + { + var s = subviews[i]; + if (s.Frame.Height > 1) + count++; + } + + if (count <= 1) + return; + + _backgroundView = subviews[0]; + _backgroundView.RemoveFromSuperview(); + + var cell = GetContextCell(scrollView); + cell.SelectionStyle = UITableViewCellSelectionStyle.None; + } + + void RestoreHighlight(UIScrollView scrollView) + { + if (_backgroundView == null) + return; + + var cell = GetContextCell(scrollView); + cell.SelectionStyle = UITableViewCellSelectionStyle.Default; + cell.SetSelected(true, false); + + scrollView.Superview.Superview.InsertSubview(_backgroundView, 0); + _backgroundView = null; + } + + void SetButtonsShowing(bool show) + { + for (var i = 0; i < _buttons.Count; i++) + _buttons[i].Hidden = !show; + } + } +}
\ No newline at end of file diff --git a/Xamarin.Forms.Platform.iOS/Deserializer.cs b/Xamarin.Forms.Platform.iOS/Deserializer.cs new file mode 100644 index 00000000..bdab44df --- /dev/null +++ b/Xamarin.Forms.Platform.iOS/Deserializer.cs @@ -0,0 +1,89 @@ +using System; +using System.Collections.Generic; +using System.Diagnostics; +using System.IO.IsolatedStorage; +using System.Runtime.Serialization; +using System.Threading.Tasks; +using System.Xml; + +namespace Xamarin.Forms.Platform.iOS +{ + internal class Deserializer : IDeserializer + { + const string PropertyStoreFile = "PropertyStore.forms"; + + public Task<IDictionary<string, object>> DeserializePropertiesAsync() + { + // Deserialize property dictionary to local storage + // Make sure to use Internal + return Task.Run(() => + { + using(var store = IsolatedStorageFile.GetUserStoreForApplication()) + using(var stream = store.OpenFile(PropertyStoreFile, System.IO.FileMode.OpenOrCreate)) + using(var reader = XmlDictionaryReader.CreateBinaryReader(stream, XmlDictionaryReaderQuotas.Max)) + { + if (stream.Length == 0) + return null; + + try + { + var dcs = new DataContractSerializer(typeof(Dictionary<string, object>)); + return (IDictionary<string, object>)dcs.ReadObject(reader); + } + catch (Exception e) + { + Debug.WriteLine("Could not deserialize properties: " + e.Message); + Log.Warning("Xamarin.Forms PropertyStore", $"Exception while reading Application properties: {e}"); + } + } + + return null; + }); + } + + public Task SerializePropertiesAsync(IDictionary<string, object> properties) + { + properties = new Dictionary<string, object>(properties); + // Serialize property dictionary to local storage + // Make sure to use Internal + return Task.Run(() => + { + var success = false; + using(var store = IsolatedStorageFile.GetUserStoreForApplication()) + using(var stream = store.OpenFile(PropertyStoreFile + ".tmp", System.IO.FileMode.OpenOrCreate)) + using(var writer = XmlDictionaryWriter.CreateBinaryWriter(stream)) + { + try + { + var dcs = new DataContractSerializer(typeof(Dictionary<string, object>)); + dcs.WriteObject(writer, properties); + writer.Flush(); + success = true; + } + catch (Exception e) + { + Debug.WriteLine("Could not serialize properties: " + e.Message); + Log.Warning("Xamarin.Forms PropertyStore", $"Exception while writing Application properties: {e}"); + } + } + + if (!success) + return; + using(var store = IsolatedStorageFile.GetUserStoreForApplication()) + { + try + { + if (store.FileExists(PropertyStoreFile)) + store.DeleteFile(PropertyStoreFile); + store.MoveFile(PropertyStoreFile + ".tmp", PropertyStoreFile); + } + catch (Exception e) + { + Debug.WriteLine("Could not move new serialized property file over old: " + e.Message); + Log.Warning("Xamarin.Forms PropertyStore", $"Exception while writing Application properties: {e}"); + } + } + }); + } + } +}
\ No newline at end of file diff --git a/Xamarin.Forms.Platform.iOS/ElementChangedEventArgs.cs b/Xamarin.Forms.Platform.iOS/ElementChangedEventArgs.cs new file mode 100644 index 00000000..560cfd1c --- /dev/null +++ b/Xamarin.Forms.Platform.iOS/ElementChangedEventArgs.cs @@ -0,0 +1,24 @@ +using System; + +namespace Xamarin.Forms.Platform.iOS +{ + public class VisualElementChangedEventArgs : ElementChangedEventArgs<VisualElement> + { + public VisualElementChangedEventArgs(VisualElement oldElement, VisualElement newElement) : base(oldElement, newElement) + { + } + } + + public class ElementChangedEventArgs<TElement> : EventArgs where TElement : Element + { + public ElementChangedEventArgs(TElement oldElement, TElement newElement) + { + OldElement = oldElement; + NewElement = newElement; + } + + public TElement NewElement { get; private set; } + + public TElement OldElement { get; private set; } + } +}
\ No newline at end of file diff --git a/Xamarin.Forms.Platform.iOS/EventTracker.cs b/Xamarin.Forms.Platform.iOS/EventTracker.cs new file mode 100644 index 00000000..16ccd505 --- /dev/null +++ b/Xamarin.Forms.Platform.iOS/EventTracker.cs @@ -0,0 +1,303 @@ +using System; +using System.Collections.Generic; +using System.Collections.ObjectModel; +using System.Collections.Specialized; +using System.Linq; +#if __UNIFIED__ +using UIKit; +#else +using MonoTouch.UIKit; +#endif +#if __UNIFIED__ +using RectangleF = CoreGraphics.CGRect; +using SizeF = CoreGraphics.CGSize; +using PointF = CoreGraphics.CGPoint; + +#else +using nfloat=System.Single; +using nint=System.Int32; +using nuint=System.UInt32; +#endif + +namespace Xamarin.Forms.Platform.iOS +{ + public class EventTracker : IDisposable + { + readonly NotifyCollectionChangedEventHandler _collectionChangedHandler; + + readonly Dictionary<IGestureRecognizer, UIGestureRecognizer> _gestureRecognizers = new Dictionary<IGestureRecognizer, UIGestureRecognizer>(); + + readonly IVisualElementRenderer _renderer; + bool _disposed; + UIView _handler; + + double _previousScale = 1.0; + UITouchEventArgs _shouldReceive; + + public EventTracker(IVisualElementRenderer renderer) + { + if (renderer == null) + throw new ArgumentNullException("renderer"); + + _collectionChangedHandler = ModelGestureRecognizersOnCollectionChanged; + + _renderer = renderer; + _renderer.ElementChanged += OnElementChanged; + } + + ObservableCollection<IGestureRecognizer> ElementGestureRecognizers + { + get + { + if (_renderer?.Element is View) + return ((View)_renderer.Element).GestureRecognizers as ObservableCollection<IGestureRecognizer>; + return null; + } + } + + public void Dispose() + { + if (_disposed) + return; + + _disposed = true; + + foreach (var kvp in _gestureRecognizers) + { + _handler.RemoveGestureRecognizer(kvp.Value); + kvp.Value.Dispose(); + } + + _gestureRecognizers.Clear(); + + if (ElementGestureRecognizers != null) + ElementGestureRecognizers.CollectionChanged -= _collectionChangedHandler; + + _handler = null; + } + + public void LoadEvents(UIView handler) + { + if (_disposed) + throw new ObjectDisposedException(null); + + _shouldReceive = (r, t) => t.View is IVisualElementRenderer; + + _handler = handler; + OnElementChanged(this, new VisualElementChangedEventArgs(null, _renderer.Element)); + } + + protected virtual UIGestureRecognizer GetNativeRecognizer(IGestureRecognizer recognizer) + { + if (recognizer == null) + return null; + + var weakRecognizer = new WeakReference(recognizer); + var weakEventTracker = new WeakReference(this); + + var tapRecognizer = recognizer as TapGestureRecognizer; + if (tapRecognizer != null) + { + var uiRecognizer = CreateTapRecognizer(1, tapRecognizer.NumberOfTapsRequired, r => + { + var tapGestureRecognizer = weakRecognizer.Target as TapGestureRecognizer; + var eventTracker = weakEventTracker.Target as EventTracker; + var view = eventTracker?._renderer?.Element as View; + + if (tapGestureRecognizer != null && view != null) + tapGestureRecognizer.SendTapped(view); + }); + return uiRecognizer; + } + + var pinchRecognizer = recognizer as PinchGestureRecognizer; + if (pinchRecognizer != null) + { + double startingScale = 1; + var uiRecognizer = CreatePinchRecognizer(r => + { + var pinchGestureRecognizer = weakRecognizer.Target as IPinchGestureController; + var eventTracker = weakEventTracker.Target as EventTracker; + var view = eventTracker?._renderer?.Element as View; + + if (pinchGestureRecognizer != null && eventTracker != null && view != null) + { + var oldScale = eventTracker._previousScale; + var originPoint = r.LocationInView(null); + originPoint = UIApplication.SharedApplication.KeyWindow.ConvertPointToView(originPoint, eventTracker._renderer.NativeView); + var scaledPoint = new Point(originPoint.X / view.Width, originPoint.Y / view.Height); + + switch (r.State) + { + case UIGestureRecognizerState.Began: + if (r.NumberOfTouches < 2) + return; + pinchGestureRecognizer.SendPinchStarted(view, scaledPoint); + startingScale = view.Scale; + break; + case UIGestureRecognizerState.Changed: + if (r.NumberOfTouches < 2 && pinchGestureRecognizer.IsPinching) + { + r.State = UIGestureRecognizerState.Ended; + pinchGestureRecognizer.SendPinchEnded(view); + return; + } + + var delta = 1.0; + var dif = Math.Abs(r.Scale - oldScale) * startingScale; + if (oldScale < r.Scale) + delta = 1 + dif; + if (oldScale > r.Scale) + delta = 1 - dif; + + pinchGestureRecognizer.SendPinch(view, delta, scaledPoint); + eventTracker._previousScale = r.Scale; + break; + case UIGestureRecognizerState.Cancelled: + case UIGestureRecognizerState.Failed: + if (pinchGestureRecognizer.IsPinching) + pinchGestureRecognizer.SendPinchCanceled(view); + break; + case UIGestureRecognizerState.Ended: + if (pinchGestureRecognizer.IsPinching) + pinchGestureRecognizer.SendPinchEnded(view); + eventTracker._previousScale = 1; + break; + } + } + }); + return uiRecognizer; + } + + var panRecognizer = recognizer as PanGestureRecognizer; + if (panRecognizer != null) + { + var uiRecognizer = CreatePanRecognizer(panRecognizer.TouchPoints, r => + { + var eventTracker = weakEventTracker.Target as EventTracker; + var view = eventTracker?._renderer?.Element as View; + + var panGestureRecognizer = weakRecognizer.Target as IPanGestureController; + if (panGestureRecognizer != null && view != null) + { + switch (r.State) + { + case UIGestureRecognizerState.Began: + if (r.NumberOfTouches != panRecognizer.TouchPoints) + return; + panGestureRecognizer.SendPanStarted(view, Application.Current.PanGestureId); + break; + case UIGestureRecognizerState.Changed: + if (r.NumberOfTouches != panRecognizer.TouchPoints) + { + r.State = UIGestureRecognizerState.Ended; + panGestureRecognizer.SendPanCompleted(view, Application.Current.PanGestureId); + Application.Current.PanGestureId++; + return; + } + var translationInView = r.TranslationInView(_handler); + panGestureRecognizer.SendPan(view, translationInView.X, translationInView.Y, Application.Current.PanGestureId); + break; + case UIGestureRecognizerState.Cancelled: + case UIGestureRecognizerState.Failed: + panGestureRecognizer.SendPanCanceled(view, Application.Current.PanGestureId); + Application.Current.PanGestureId++; + break; + case UIGestureRecognizerState.Ended: + if (r.NumberOfTouches != panRecognizer.TouchPoints) + { + panGestureRecognizer.SendPanCompleted(view, Application.Current.PanGestureId); + Application.Current.PanGestureId++; + } + break; + } + } + }); + return uiRecognizer; + } + + return null; + } + + UIPanGestureRecognizer CreatePanRecognizer(int numTouches, Action<UIPanGestureRecognizer> action) + { + var result = new UIPanGestureRecognizer(action); + result.MinimumNumberOfTouches = result.MaximumNumberOfTouches = (uint)numTouches; + return result; + } + + UIPinchGestureRecognizer CreatePinchRecognizer(Action<UIPinchGestureRecognizer> action) + { + var result = new UIPinchGestureRecognizer(action); + return result; + } + + UITapGestureRecognizer CreateTapRecognizer(int numFingers, int numTaps, Action<UITapGestureRecognizer> action) + { + var result = new UITapGestureRecognizer(action); + result.NumberOfTouchesRequired = (uint)numFingers; + result.NumberOfTapsRequired = (uint)numTaps; + return result; + } + + void LoadRecognizers() + { + if (ElementGestureRecognizers == null) + return; + + foreach (var recognizer in ElementGestureRecognizers) + { + if (_gestureRecognizers.ContainsKey(recognizer)) + continue; + + var nativeRecognizer = GetNativeRecognizer(recognizer); + if (nativeRecognizer != null) + { + nativeRecognizer.ShouldReceiveTouch = _shouldReceive; + _handler.AddGestureRecognizer(nativeRecognizer); + + _gestureRecognizers[recognizer] = nativeRecognizer; + } + } + + var toRemove = _gestureRecognizers.Keys.Where(key => !ElementGestureRecognizers.Contains(key)).ToArray(); + foreach (var gestureRecognizer in toRemove) + { + var uiRecognizer = _gestureRecognizers[gestureRecognizer]; + _gestureRecognizers.Remove(gestureRecognizer); + + _handler.RemoveGestureRecognizer(uiRecognizer); + uiRecognizer.Dispose(); + } + } + + void ModelGestureRecognizersOnCollectionChanged(object sender, NotifyCollectionChangedEventArgs notifyCollectionChangedEventArgs) + { + LoadRecognizers(); + } + + void OnElementChanged(object sender, VisualElementChangedEventArgs e) + { + if (e.OldElement != null) + { + // unhook + var oldView = e.OldElement as View; + if (oldView != null) + { + var oldRecognizers = (ObservableCollection<IGestureRecognizer>)oldView.GestureRecognizers; + oldRecognizers.CollectionChanged -= _collectionChangedHandler; + } + } + + if (e.NewElement != null) + { + // hook + if (ElementGestureRecognizers != null) + { + ElementGestureRecognizers.CollectionChanged += _collectionChangedHandler; + LoadRecognizers(); + } + } + } + } +}
\ No newline at end of file diff --git a/Xamarin.Forms.Platform.iOS/ExportCellAttribute.cs b/Xamarin.Forms.Platform.iOS/ExportCellAttribute.cs new file mode 100644 index 00000000..96aec6cb --- /dev/null +++ b/Xamarin.Forms.Platform.iOS/ExportCellAttribute.cs @@ -0,0 +1,12 @@ +using System; + +namespace Xamarin.Forms +{ + [AttributeUsage(AttributeTargets.Assembly, AllowMultiple = true)] + public sealed class ExportCellAttribute : HandlerAttribute + { + public ExportCellAttribute(Type handler, Type target) : base(handler, target) + { + } + } +}
\ No newline at end of file diff --git a/Xamarin.Forms.Platform.iOS/ExportImageSourceHandlerAttribute.cs b/Xamarin.Forms.Platform.iOS/ExportImageSourceHandlerAttribute.cs new file mode 100644 index 00000000..4c5c01fb --- /dev/null +++ b/Xamarin.Forms.Platform.iOS/ExportImageSourceHandlerAttribute.cs @@ -0,0 +1,12 @@ +using System; + +namespace Xamarin.Forms +{ + [AttributeUsage(AttributeTargets.Assembly, AllowMultiple = true)] + public sealed class ExportImageSourceHandlerAttribute : HandlerAttribute + { + public ExportImageSourceHandlerAttribute(Type handler, Type target) : base(handler, target) + { + } + } +}
\ No newline at end of file diff --git a/Xamarin.Forms.Platform.iOS/ExportRendererAttribute.cs b/Xamarin.Forms.Platform.iOS/ExportRendererAttribute.cs new file mode 100644 index 00000000..50bba17f --- /dev/null +++ b/Xamarin.Forms.Platform.iOS/ExportRendererAttribute.cs @@ -0,0 +1,29 @@ +using System; +using UIKit; + +namespace Xamarin.Forms +{ + [AttributeUsage(AttributeTargets.Assembly, AllowMultiple = true)] + public sealed class ExportRendererAttribute : HandlerAttribute + { + public ExportRendererAttribute(Type handler, Type target, UIUserInterfaceIdiom idiom) : base(handler, target) + { + Idiomatic = true; + Idiom = idiom; + } + + public ExportRendererAttribute(Type handler, Type target) : base(handler, target) + { + Idiomatic = false; + } + + internal UIUserInterfaceIdiom Idiom { get; } + + internal bool Idiomatic { get; } + + public override bool ShouldRegister() + { + return !Idiomatic || Idiom == UIDevice.CurrentDevice.UserInterfaceIdiom; + } + } +}
\ No newline at end of file diff --git a/Xamarin.Forms.Platform.iOS/Extensions/ArrayExtensions.cs b/Xamarin.Forms.Platform.iOS/Extensions/ArrayExtensions.cs new file mode 100644 index 00000000..9adafecb --- /dev/null +++ b/Xamarin.Forms.Platform.iOS/Extensions/ArrayExtensions.cs @@ -0,0 +1,38 @@ +using System; + +namespace Xamarin.Forms.Platform.iOS +{ + internal static class ArrayExtensions + { + public static T[] Insert<T>(this T[] self, int index, T item) + { + var result = new T[self.Length + 1]; + if (index > 0) + Array.Copy(self, result, index); + + result[index] = item; + + if (index < self.Length) + Array.Copy(self, index, result, index + 1, result.Length - index - 1); + + return result; + } + + public static T[] Remove<T>(this T[] self, T item) + { + return self.RemoveAt(self.IndexOf(item)); + } + + public static T[] RemoveAt<T>(this T[] self, int index) + { + var result = new T[self.Length - 1]; + if (index > 0) + Array.Copy(self, result, index); + + if (index < self.Length - 1) + Array.Copy(self, index + 1, result, index, self.Length - index - 1); + + return result; + } + } +}
\ No newline at end of file diff --git a/Xamarin.Forms.Platform.iOS/Extensions/CellExtensions.cs b/Xamarin.Forms.Platform.iOS/Extensions/CellExtensions.cs new file mode 100644 index 00000000..ca05bc54 --- /dev/null +++ b/Xamarin.Forms.Platform.iOS/Extensions/CellExtensions.cs @@ -0,0 +1,41 @@ +using System; +#if __UNIFIED__ +using Foundation; + +#else +using MonoTouch.Foundation; +#endif + +namespace Xamarin.Forms.Platform.iOS +{ + internal static class CellExtensions + { + internal static NSIndexPath GetIndexPath(this Cell self) + { + if (self == null) + throw new ArgumentNullException("self"); + + NSIndexPath path; + + if (self.RealParent is ListView) + { + var section = 0; + var til = TemplatedItemsList<ItemsView<Cell>, Cell>.GetGroup(self); + if (til != null) + section = TemplatedItemsList<ItemsView<Cell>, Cell>.GetIndex(til.HeaderContent); + + var row = TemplatedItemsList<ItemsView<Cell>, Cell>.GetIndex(self); + path = NSIndexPath.FromRowSection(row, section); + } + else if (self.RealParent is TableView) + { + var tmPath = TableView.TableSectionModel.GetPath(self); + path = NSIndexPath.FromRowSection(tmPath.Item2, tmPath.Item1); + } + else + throw new NotSupportedException("Unknown cell parent type"); + + return path; + } + } +}
\ No newline at end of file diff --git a/Xamarin.Forms.Platform.iOS/Extensions/ColorExtensions.cs b/Xamarin.Forms.Platform.iOS/Extensions/ColorExtensions.cs new file mode 100644 index 00000000..f512e157 --- /dev/null +++ b/Xamarin.Forms.Platform.iOS/Extensions/ColorExtensions.cs @@ -0,0 +1,99 @@ +using System; +using System.ComponentModel; +using System.Drawing; +using System.Linq; +#if __UNIFIED__ +using CoreAnimation; +using CoreGraphics; +using Foundation; +using UIKit; +#else +using MonoTouch.CoreAnimation; +using MonoTouch.CoreGraphics; +using MonoTouch.Foundation; +using MonoTouch.UIKit; +#endif +#if __UNIFIED__ +using RectangleF = CoreGraphics.CGRect; +using SizeF = CoreGraphics.CGSize; +using PointF = CoreGraphics.CGPoint; + +#else +using nfloat=System.Single; +using nint=System.Int32; +using nuint=System.UInt32; +#endif + +namespace Xamarin.Forms.Platform.iOS +{ + public static class ColorExtensions + { + internal static readonly UIColor Black = UIColor.Black; + internal static readonly UIColor SeventyPercentGrey = new UIColor(0.7f, 0.7f, 0.7f, 1); + + public static CGColor ToCGColor(this Color color) + { + return new CGColor((float)color.R, (float)color.G, (float)color.B, (float)color.A); + } + + public static Color ToColor(this UIColor color) + { + nfloat red; + nfloat green; + nfloat blue; + nfloat alpha; + color.GetRGBA(out red, out green, out blue, out alpha); + return new Color(red, green, blue, alpha); + } + + public static UIColor ToUIColor(this Color color) + { + return new UIColor((float)color.R, (float)color.G, (float)color.B, (float)color.A); + } + + public static UIColor ToUIColor(this Color color, Color defaultColor) + { + if (color.IsDefault) + return defaultColor.ToUIColor(); + + return color.ToUIColor(); + } + + public static UIColor ToUIColor(this Color color, UIColor defaultColor) + { + if (color.IsDefault) + return defaultColor; + + return color.ToUIColor(); + } + } + + public static class PointExtensions + { + public static Point ToPoint(this PointF point) + { + return new Point(point.X, point.Y); + } + } + + public static class SizeExtensions + { + public static SizeF ToSizeF(this Size size) + { + return new SizeF((float)size.Width, (float)size.Height); + } + } + + public static class RectangleExtensions + { + public static Rectangle ToRectangle(this RectangleF rect) + { + return new Rectangle(rect.X, rect.Y, rect.Width, rect.Height); + } + + public static RectangleF ToRectangleF(this Rectangle rect) + { + return new RectangleF((nfloat)rect.X, (nfloat)rect.Y, (nfloat)rect.Width, (nfloat)rect.Height); + } + } +}
\ No newline at end of file diff --git a/Xamarin.Forms.Platform.iOS/Extensions/DateExtensions.cs b/Xamarin.Forms.Platform.iOS/Extensions/DateExtensions.cs new file mode 100644 index 00000000..a75e7a69 --- /dev/null +++ b/Xamarin.Forms.Platform.iOS/Extensions/DateExtensions.cs @@ -0,0 +1,33 @@ +using System; +using System.Collections.Generic; +using System.ComponentModel; +using System.Drawing; +using System.Linq; +#if __UNIFIED__ +using CoreAnimation; +using CoreGraphics; +using Foundation; +using UIKit; + +#else +using MonoTouch.CoreAnimation; +using MonoTouch.CoreGraphics; +using MonoTouch.Foundation; +using MonoTouch.UIKit; +#endif + +namespace Xamarin.Forms.Platform.iOS +{ + public static class DateExtensions + { + public static DateTime ToDateTime(this NSDate date) + { + return new DateTime(2001, 1, 1, 0, 0, 0).AddSeconds(date.SecondsSinceReferenceDate); + } + + public static NSDate ToNSDate(this DateTime date) + { + return NSDate.FromTimeIntervalSinceReferenceDate((date - new DateTime(2001, 1, 1, 0, 0, 0)).TotalSeconds); + } + } +}
\ No newline at end of file diff --git a/Xamarin.Forms.Platform.iOS/Extensions/Extensions.cs b/Xamarin.Forms.Platform.iOS/Extensions/Extensions.cs new file mode 100644 index 00000000..e74fed8d --- /dev/null +++ b/Xamarin.Forms.Platform.iOS/Extensions/Extensions.cs @@ -0,0 +1,76 @@ +using System; +using System.Collections.Generic; +#if __UNIFIED__ +using UIKit; + +#else +using MonoTouch.UIKit; +#endif + +namespace Xamarin.Forms.Platform.iOS +{ + public static class Extensions + { + public static void ApplyKeyboard(this IUITextInput textInput, Keyboard keyboard) + { + textInput.AutocapitalizationType = UITextAutocapitalizationType.None; + textInput.AutocorrectionType = UITextAutocorrectionType.No; + textInput.SpellCheckingType = UITextSpellCheckingType.No; + + if (keyboard == Keyboard.Default) + { + textInput.AutocapitalizationType = UITextAutocapitalizationType.Sentences; + textInput.AutocorrectionType = UITextAutocorrectionType.Default; + textInput.SpellCheckingType = UITextSpellCheckingType.Default; + textInput.KeyboardType = UIKeyboardType.Default; + } + else if (keyboard == Keyboard.Chat) + { + textInput.AutocapitalizationType = UITextAutocapitalizationType.Sentences; + textInput.AutocorrectionType = UITextAutocorrectionType.Yes; + } + else if (keyboard == Keyboard.Email) + textInput.KeyboardType = UIKeyboardType.EmailAddress; + else if (keyboard == Keyboard.Numeric) + textInput.KeyboardType = UIKeyboardType.DecimalPad; + else if (keyboard == Keyboard.Telephone) + textInput.KeyboardType = UIKeyboardType.PhonePad; + else if (keyboard == Keyboard.Text) + { + textInput.AutocapitalizationType = UITextAutocapitalizationType.Sentences; + textInput.AutocorrectionType = UITextAutocorrectionType.Yes; + textInput.SpellCheckingType = UITextSpellCheckingType.Yes; + } + else if (keyboard == Keyboard.Url) + textInput.KeyboardType = UIKeyboardType.Url; + else if (keyboard is CustomKeyboard) + { + var custom = (CustomKeyboard)keyboard; + var capitalizedSentenceEnabled = (custom.Flags & KeyboardFlags.CapitalizeSentence) == KeyboardFlags.CapitalizeSentence; + var spellcheckEnabled = (custom.Flags & KeyboardFlags.Spellcheck) == KeyboardFlags.Spellcheck; + var suggestionsEnabled = (custom.Flags & KeyboardFlags.Suggestions) == KeyboardFlags.Suggestions; + + textInput.AutocapitalizationType = capitalizedSentenceEnabled ? UITextAutocapitalizationType.Sentences : UITextAutocapitalizationType.None; + textInput.AutocorrectionType = suggestionsEnabled ? UITextAutocorrectionType.Yes : UITextAutocorrectionType.No; + textInput.SpellCheckingType = spellcheckEnabled ? UITextSpellCheckingType.Yes : UITextSpellCheckingType.No; + } + } + + internal static DeviceOrientation ToDeviceOrientation(this UIDeviceOrientation orientation) + { + switch (orientation) + { + case UIDeviceOrientation.Portrait: + return DeviceOrientation.Portrait; + case UIDeviceOrientation.PortraitUpsideDown: + return DeviceOrientation.PortraitDown; + case UIDeviceOrientation.LandscapeLeft: + return DeviceOrientation.LandscapeLeft; + case UIDeviceOrientation.LandscapeRight: + return DeviceOrientation.LandscapeRight; + default: + return DeviceOrientation.Other; + } + } + } +}
\ No newline at end of file diff --git a/Xamarin.Forms.Platform.iOS/Extensions/ToolbarItemExtensions.cs b/Xamarin.Forms.Platform.iOS/Extensions/ToolbarItemExtensions.cs new file mode 100644 index 00000000..71bdbbac --- /dev/null +++ b/Xamarin.Forms.Platform.iOS/Extensions/ToolbarItemExtensions.cs @@ -0,0 +1,228 @@ +using System; +using System.Drawing; +using System.ComponentModel; +#if __UNIFIED__ +using CoreGraphics; +using UIKit; +#else +using MonoTouch.CoreGraphics; +using MonoTouch.UIKit; +#endif +#if __UNIFIED__ +using RectangleF = CoreGraphics.CGRect; +using SizeF = CoreGraphics.CGSize; +using PointF = CoreGraphics.CGPoint; + +#else +using nfloat=System.Single; +using nint=System.Int32; +using nuint=System.UInt32; +#endif + +namespace Xamarin.Forms.Platform.iOS +{ + public static class ToolbarItemExtensions + { + public static UIBarButtonItem ToUIBarButtonItem(this ToolbarItem item, bool forceName = false) + { + return item.Order == ToolbarItemOrder.Secondary ? new SecondaryToolbarItem(item) : (UIBarButtonItem)new PrimaryToolbarItem(item, forceName); + } + + sealed class PrimaryToolbarItem : UIBarButtonItem + { + readonly bool _forceName; + readonly ToolbarItem _item; + + public PrimaryToolbarItem(ToolbarItem item, bool forceName) + { + _forceName = forceName; + _item = item; + + if (!string.IsNullOrEmpty(item.Icon) && !forceName) + UpdateIconAndStyle(); + else + UpdateTextAndStyle(); + UpdateIsEnabled(); + + Clicked += (sender, e) => item.Activate(); + item.PropertyChanged += OnPropertyChanged; + + if (item != null && !string.IsNullOrEmpty(item.AutomationId)) + AccessibilityIdentifier = item.AutomationId; + } + + protected override void Dispose(bool disposing) + { + if (disposing) + _item.PropertyChanged -= OnPropertyChanged; + base.Dispose(disposing); + } + + void OnPropertyChanged(object sender, PropertyChangedEventArgs e) + { + if (e.PropertyName == MenuItem.IsEnabledProperty.PropertyName) + UpdateIsEnabled(); + else if (e.PropertyName == MenuItem.TextProperty.PropertyName) + { + if (string.IsNullOrEmpty(_item.Icon) || _forceName) + UpdateTextAndStyle(); + } + else if (e.PropertyName == MenuItem.IconProperty.PropertyName) + { + if (!_forceName) + { + if (!string.IsNullOrEmpty(_item.Icon)) + UpdateIconAndStyle(); + else + UpdateTextAndStyle(); + } + } + } + + void UpdateIconAndStyle() + { + var image = UIImage.FromBundle(_item.Icon); + Image = image; + Style = UIBarButtonItemStyle.Plain; + } + + void UpdateIsEnabled() + { + Enabled = _item.IsEnabled; + } + + void UpdateTextAndStyle() + { + Title = _item.Text; + Style = UIBarButtonItemStyle.Bordered; + Image = null; + } + } + + sealed class SecondaryToolbarItem : UIBarButtonItem + { + readonly ToolbarItem _item; + + public SecondaryToolbarItem(ToolbarItem item) : base(new SecondaryToolbarItemContent()) + { + _item = item; + UpdateText(); + UpdateIcon(); + UpdateIsEnabled(); + + ((SecondaryToolbarItemContent)CustomView).TouchUpInside += (sender, e) => item.Activate(); + item.PropertyChanged += OnPropertyChanged; + + if (item != null && !string.IsNullOrEmpty(item.AutomationId)) + AccessibilityIdentifier = item.AutomationId; + } + + protected override void Dispose(bool disposing) + { + if (disposing) + _item.PropertyChanged -= OnPropertyChanged; + base.Dispose(disposing); + } + + void OnPropertyChanged(object sender, PropertyChangedEventArgs e) + { + if (e.PropertyName == MenuItem.TextProperty.PropertyName) + UpdateText(); + else if (e.PropertyName == MenuItem.IconProperty.PropertyName) + UpdateIcon(); + else if (e.PropertyName == MenuItem.IsEnabledProperty.PropertyName) + UpdateIsEnabled(); + } + + void UpdateIcon() + { + ((SecondaryToolbarItemContent)CustomView).Image = string.IsNullOrEmpty(_item.Icon) ? null : new UIImage(_item.Icon); + } + + void UpdateIsEnabled() + { + ((UIControl)CustomView).Enabled = _item.IsEnabled; + } + + void UpdateText() + { + ((SecondaryToolbarItemContent)CustomView).Text = _item.Text; + } + + sealed class SecondaryToolbarItemContent : UIControl + { + readonly UIImageView _imageView; + readonly UILabel _label; + + public SecondaryToolbarItemContent() : base(new RectangleF(0, 0, 75, 20)) + { + BackgroundColor = UIColor.Clear; + _imageView = new UIImageView { BackgroundColor = UIColor.Clear }; + AddSubview(_imageView); + + _label = new UILabel { BackgroundColor = UIColor.Clear, Lines = 1, LineBreakMode = UILineBreakMode.TailTruncation, Font = UIFont.SystemFontOfSize(10) }; + AddSubview(_label); + } + + public override bool Enabled + { + get { return base.Enabled; } + set + { + base.Enabled = value; + _label.Enabled = value; + _imageView.Alpha = value ? 1f : 0.25f; + } + } + + public UIImage Image + { + get { return _imageView.Image; } + set { _imageView.Image = value; } + } + + public string Text + { + get { return _label.Text; } + set { _label.Text = value; } + } + + public override void LayoutSubviews() + { + base.LayoutSubviews(); + + const float padding = 5f; + var imageSize = _imageView.SizeThatFits(Bounds.Size); + var fullStringSize = _label.SizeThatFits(Bounds.Size); + + if (imageSize.Width > 0 && (string.IsNullOrEmpty(Text) || fullStringSize.Width > Bounds.Width / 3)) + { + _imageView.Frame = new RectangleF(PointF.Empty, imageSize); + _imageView.Center = new PointF(Bounds.GetMidX(), Bounds.GetMidY()); + _label.Hidden = true; + return; + } + + _label.Hidden = false; + var availableWidth = Bounds.Width - padding * 3 - imageSize.Width; + var stringSize = _label.SizeThatFits(new SizeF(availableWidth, Bounds.Height - padding * 2)); + + availableWidth = Bounds.Width; + availableWidth -= stringSize.Width; + availableWidth -= imageSize.Width; + + var x = availableWidth / 2; + + var frame = new RectangleF(new PointF(x, Bounds.GetMidY() - imageSize.Height / 2), imageSize); + _imageView.Frame = frame; + + frame.X = frame.Right + (imageSize.Width > 0 ? padding : 0); + frame.Size = stringSize; + frame.Height = Bounds.Height; + frame.Y = 0; + _label.Frame = frame; + } + } + } + } +}
\ No newline at end of file diff --git a/Xamarin.Forms.Platform.iOS/Extensions/UIViewExtensions.cs b/Xamarin.Forms.Platform.iOS/Extensions/UIViewExtensions.cs new file mode 100644 index 00000000..9847867c --- /dev/null +++ b/Xamarin.Forms.Platform.iOS/Extensions/UIViewExtensions.cs @@ -0,0 +1,66 @@ +using System.Collections.Generic; +using System.Drawing; +using System.Linq; +using System; +#if __UNIFIED__ +using UIKit; + +#else +using MonoTouch.UIKit; +#endif + +namespace Xamarin.Forms.Platform.iOS +{ + 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) + { + var s = self.SizeThatFits(new SizeF((float)widthConstraint, (float)heightConstraint)); + 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); + } + + 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; + } + + 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; + } + } +}
\ No newline at end of file diff --git a/Xamarin.Forms.Platform.iOS/Extensions/ViewExtensions.cs b/Xamarin.Forms.Platform.iOS/Extensions/ViewExtensions.cs new file mode 100644 index 00000000..9ded0632 --- /dev/null +++ b/Xamarin.Forms.Platform.iOS/Extensions/ViewExtensions.cs @@ -0,0 +1,20 @@ +using System.Collections.Generic; + +namespace Xamarin.Forms.Platform.iOS +{ + public static class ViewExtensions + { + public static IEnumerable<Page> GetParentPages(this Page target) + { + var result = new List<Page>(); + var parent = target.RealParent as Page; + while (!Application.IsApplicationOrNull(parent)) + { + result.Add(parent); + parent = parent.RealParent as Page; + } + + return result; + } + } +}
\ No newline at end of file diff --git a/Xamarin.Forms.Platform.iOS/Forms.cs b/Xamarin.Forms.Platform.iOS/Forms.cs new file mode 100644 index 00000000..b7a41826 --- /dev/null +++ b/Xamarin.Forms.Platform.iOS/Forms.cs @@ -0,0 +1,373 @@ +using System; +using System.Collections.Generic; +using System.Diagnostics; +using System.IO; +using System.IO.IsolatedStorage; +using System.Linq; +using System.Linq.Expressions; +using System.Net.Http; +using System.Reflection; +using System.Security.Cryptography; +using System.Text; +using System.Threading; +using System.Threading.Tasks; +#if __UNIFIED__ +using UIKit; +using CoreFoundation; +using Foundation; +#else +using MonoTouch.UIKit; +using MonoTouch.CoreFoundation; +using MonoTouch.Foundation; +#endif +using Xamarin.Forms.Platform.iOS; + +namespace Xamarin.Forms +{ + public static class Forms + { + //Preserve GetCallingAssembly + static readonly bool nevertrue = false; + + static bool? s_isiOS7OrNewer; + + static bool? s_isiOS8OrNewer; + + static bool? s_isiOS9OrNewer; + + static Forms() + { + if (nevertrue) + Assembly.GetCallingAssembly(); + } + + public static bool IsInitialized { get; private set; } + + internal static bool IsiOS7OrNewer + { + get + { + if (!s_isiOS7OrNewer.HasValue) + s_isiOS7OrNewer = UIDevice.CurrentDevice.CheckSystemVersion(7, 0); + return s_isiOS7OrNewer.Value; + } + } + + internal static bool IsiOS8OrNewer + { + get + { + if (!s_isiOS8OrNewer.HasValue) + s_isiOS8OrNewer = UIDevice.CurrentDevice.CheckSystemVersion(8, 0); + return s_isiOS8OrNewer.Value; + } + } + + internal static bool IsiOS9OrNewer + { + get + { + if (!s_isiOS9OrNewer.HasValue) + s_isiOS9OrNewer = UIDevice.CurrentDevice.CheckSystemVersion(9, 0); + return s_isiOS9OrNewer.Value; + } + } + + public static void Init() + { + if (IsInitialized) + return; + IsInitialized = true; + Color.Accent = Color.FromRgba(50, 79, 133, 255); + + Log.Listeners.Add(new DelegateLogListener((c, m) => Trace.WriteLine(m, c))); + + Device.OS = TargetPlatform.iOS; + Device.PlatformServices = new IOSPlatformServices(); + Device.Info = new IOSDeviceInfo(); + + Ticker.Default = new CADisplayLinkTicker(); + Registrar.RegisterAll(new[] { typeof(ExportRendererAttribute), typeof(ExportCellAttribute), typeof(ExportImageSourceHandlerAttribute) }); + + Device.Idiom = UIDevice.CurrentDevice.UserInterfaceIdiom == UIUserInterfaceIdiom.Pad ? TargetIdiom.Tablet : TargetIdiom.Phone; + + ExpressionSearch.Default = new iOSExpressionSearch(); + } + + public static event EventHandler<ViewInitializedEventArgs> ViewInitialized; + + internal static void SendViewInitialized(this VisualElement self, UIView nativeView) + { + var viewInitialized = ViewInitialized; + if (viewInitialized != null) + viewInitialized(self, new ViewInitializedEventArgs { View = self, NativeView = nativeView }); + } + + class iOSExpressionSearch : ExpressionVisitor, IExpressionSearch + { + List<object> _results; + Type _targetType; + + public List<T> FindObjects<T>(Expression expression) where T : class + { + _results = new List<object>(); + _targetType = typeof(T); + Visit(expression); + return _results.Select(o => o as T).ToList(); + } + + protected override Expression VisitMember(MemberExpression node) + { + if (node.Expression is ConstantExpression && node.Member is FieldInfo) + { + var container = ((ConstantExpression)node.Expression).Value; + var value = ((FieldInfo)node.Member).GetValue(container); + + if (_targetType.IsInstanceOfType(value)) + _results.Add(value); + } + return base.VisitMember(node); + } + } + + internal class IOSDeviceInfo : DeviceInfo + { + readonly NSObject _notification; + readonly Size _scaledScreenSize; + readonly double _scalingFactor; + + public IOSDeviceInfo() + { + _notification = UIDevice.Notifications.ObserveOrientationDidChange((sender, args) => CurrentOrientation = UIDevice.CurrentDevice.Orientation.ToDeviceOrientation()); + + _scalingFactor = UIScreen.MainScreen.Scale; + _scaledScreenSize = new Size(UIScreen.MainScreen.Bounds.Width, UIScreen.MainScreen.Bounds.Height); + PixelScreenSize = new Size(_scaledScreenSize.Width * _scalingFactor, _scaledScreenSize.Height * _scalingFactor); + } + + public override Size PixelScreenSize { get; } + + public override Size ScaledScreenSize + { + get { return _scaledScreenSize; } + } + + public override double ScalingFactor + { + get { return _scalingFactor; } + } + + protected override void Dispose(bool disposing) + { + _notification.Dispose(); + base.Dispose(disposing); + } + } + + class IOSPlatformServices : IPlatformServices + { + static readonly MD5CryptoServiceProvider Checksum = new MD5CryptoServiceProvider(); + + public void BeginInvokeOnMainThread(Action action) + { + NSRunLoop.Main.BeginInvokeOnMainThread(action.Invoke); + } + + public ITimer CreateTimer(Action<object> callback) + { + return new _Timer(new Timer(o => callback(o))); + } + + public ITimer CreateTimer(Action<object> callback, object state, int dueTime, int period) + { + return new _Timer(new Timer(o => callback(o), state, dueTime, period)); + } + + public ITimer CreateTimer(Action<object> callback, object state, long dueTime, long period) + { + return new _Timer(new Timer(o => callback(o), state, dueTime, period)); + } + + public ITimer CreateTimer(Action<object> callback, object state, TimeSpan dueTime, TimeSpan period) + { + return new _Timer(new Timer(o => callback(o), state, dueTime, period)); + } + + public ITimer CreateTimer(Action<object> callback, object state, uint dueTime, uint period) + { + return new _Timer(new Timer(o => callback(o), state, dueTime, period)); + } + + public Assembly[] GetAssemblies() + { + return AppDomain.CurrentDomain.GetAssemblies(); + } + + public string GetMD5Hash(string input) + { + var bytes = Checksum.ComputeHash(Encoding.UTF8.GetBytes(input)); + var ret = new char[32]; + for (var i = 0; i < 16; i++) + { + ret[i * 2] = (char)Hex(bytes[i] >> 4); + ret[i * 2 + 1] = (char)Hex(bytes[i] & 0xf); + } + return new string(ret); + } + + public double GetNamedSize(NamedSize size, Type targetElementType, bool useOldSizes) + { + // We make these up anyway, so new sizes didn't really change + // iOS docs say default button font size is 15, default label font size is 17 so we use those as the defaults. + switch (size) + { + case NamedSize.Default: + return typeof(Button).IsAssignableFrom(targetElementType) ? 15 : 17; + case NamedSize.Micro: + return 12; + case NamedSize.Small: + return 14; + case NamedSize.Medium: + return 17; + case NamedSize.Large: + return 22; + default: + throw new ArgumentOutOfRangeException("size"); + } + } + + public async Task<Stream> GetStreamAsync(Uri uri, CancellationToken cancellationToken) + { + using(var client = GetHttpClient()) + using(var response = await client.GetAsync(uri, cancellationToken)) + return await response.Content.ReadAsStreamAsync(); + } + + public IIsolatedStorageFile GetUserStoreForApplication() + { + return new _IsolatedStorageFile(IsolatedStorageFile.GetUserStoreForApplication()); + } + + public bool IsInvokeRequired + { + get { return !NSThread.IsMain; } + } + + public void OpenUriAction(Uri uri) + { + UIApplication.SharedApplication.OpenUrl(new NSUrl(uri.AbsoluteUri)); + } + + public void StartTimer(TimeSpan interval, Func<bool> callback) + { + NSTimer timer = null; +#if __UNIFIED__ + timer = NSTimer.CreateRepeatingScheduledTimer(interval, t => + { +#else + timer = NSTimer.CreateRepeatingScheduledTimer (interval, () => { + #endif + if (!callback()) +#if __UNIFIED__ + t.Invalidate(); +#else + timer.Invalidate (); + #endif + }); + NSRunLoop.Main.AddTimer(timer, NSRunLoopMode.Common); + } + + HttpClient GetHttpClient() + { + var proxy = CFNetwork.GetSystemProxySettings(); + var handler = new HttpClientHandler(); + if (!string.IsNullOrEmpty(proxy.HTTPProxy)) + { + handler.Proxy = CFNetwork.GetDefaultProxy(); + handler.UseProxy = true; + } + return new HttpClient(handler); + } + + static int Hex(int v) + { + if (v < 10) + return '0' + v; + return 'a' + v - 10; + } + + public class _Timer : ITimer + { + readonly Timer _timer; + + public _Timer(Timer timer) + { + _timer = timer; + } + + public void Change(int dueTime, int period) + { + _timer.Change(dueTime, period); + } + + public void Change(long dueTime, long period) + { + _timer.Change(dueTime, period); + } + + public void Change(TimeSpan dueTime, TimeSpan period) + { + _timer.Change(dueTime, period); + } + + public void Change(uint dueTime, uint period) + { + _timer.Change(dueTime, period); + } + } + + public class _IsolatedStorageFile : IIsolatedStorageFile + { + readonly IsolatedStorageFile _isolatedStorageFile; + + public _IsolatedStorageFile(IsolatedStorageFile isolatedStorageFile) + { + _isolatedStorageFile = isolatedStorageFile; + } + + public Task CreateDirectoryAsync(string path) + { + _isolatedStorageFile.CreateDirectory(path); + return Task.FromResult(true); + } + + public Task<bool> GetDirectoryExistsAsync(string path) + { + return Task.FromResult(_isolatedStorageFile.DirectoryExists(path)); + } + + public Task<bool> GetFileExistsAsync(string path) + { + return Task.FromResult(_isolatedStorageFile.FileExists(path)); + } + + public Task<DateTimeOffset> GetLastWriteTimeAsync(string path) + { + return Task.FromResult(_isolatedStorageFile.GetLastWriteTime(path)); + } + + public Task<Stream> OpenFileAsync(string path, FileMode mode, FileAccess access) + { + Stream stream = _isolatedStorageFile.OpenFile(path, (System.IO.FileMode)mode, (System.IO.FileAccess)access); + return Task.FromResult(stream); + } + + public Task<Stream> OpenFileAsync(string path, FileMode mode, FileAccess access, FileShare share) + { + Stream stream = _isolatedStorageFile.OpenFile(path, (System.IO.FileMode)mode, (System.IO.FileAccess)access, (System.IO.FileShare)share); + return Task.FromResult(stream); + } + } + } + } +}
\ No newline at end of file diff --git a/Xamarin.Forms.Platform.iOS/FormsApplicationDelegate.cs b/Xamarin.Forms.Platform.iOS/FormsApplicationDelegate.cs new file mode 100644 index 00000000..a4342473 --- /dev/null +++ b/Xamarin.Forms.Platform.iOS/FormsApplicationDelegate.cs @@ -0,0 +1,151 @@ +using System; +using System.ComponentModel; +using System.Linq; +using System.Text; +#if __UNIFIED__ +using Foundation; +using UIKit; + +#else +using MonoTouch.Foundation; +using MonoTouch.UIKit; +#endif + +namespace Xamarin.Forms.Platform.iOS +{ + public class FormsApplicationDelegate : UIApplicationDelegate + { + Application _application; + bool _isSuspended; + UIWindow _window; + + protected FormsApplicationDelegate() + { + } + + public override bool ContinueUserActivity(UIApplication application, NSUserActivity userActivity, UIApplicationRestorationHandler completionHandler) + { + return true; + } + + // now in background + public override void DidEnterBackground(UIApplication uiApplication) + { + // applicationDidEnterBackground + } + + // finish initialization before display to user + public override bool FinishedLaunching(UIApplication uiApplication, NSDictionary launchOptions) + { + // check contents of launch options and evaluate why the app was launched and respond + // initialize the important data structures + // prepare you apps window and views for display + // keep lightweight, anything long winded should be executed asynchronously on a secondary thread. + // application:didFinishLaunchingWithOptions + _window = new UIWindow(UIScreen.MainScreen.Bounds); + + if (_application == null) + throw new InvalidOperationException("You MUST invoke LoadApplication () before calling base.FinishedLaunching ()"); + + SetMainPage(); + _application.SendStart(); + return true; + } + + // about to become foreground, last minute preparatuin + public override void OnActivated(UIApplication uiApplication) + { + // applicationDidBecomeActive + // execute any OpenGL ES drawing calls + if (_application != null && _isSuspended) + { + _isSuspended = false; + _application.SendResume(); + } + } + + // transitioning to background + public override async void OnResignActivation(UIApplication uiApplication) + { + // applicationWillResignActive + if (_application != null) + { + _isSuspended = true; + await _application.SendSleepAsync(); + } + } + + public override void UserActivityUpdated(UIApplication application, NSUserActivity userActivity) + { + } + + // from background to foreground, not yet active + public override void WillEnterForeground(UIApplication uiApplication) + { + // applicationWillEnterForeground + } + + // TODO where to execute heavy code, storing state, sending to server, etc + + // first chance to execute code at launch time + public override bool WillFinishLaunching(UIApplication uiApplication, NSDictionary launchOptions) + { + // check contents of launch options and evaluate why the app was launched and respond + // initialize the important data structures + // prepare you apps window and views for display + // keep lightweight, anything long winded should be executed asynchronously on a secondary thread. + // application:willFinishLaunchingWithOptions + // Restore ui state here + return true; + } + + // app is being terminated, not called if you app is suspended + public override void WillTerminate(UIApplication uiApplication) + { + // applicationWillTerminate + //application.SendTerminate (); + } + + protected override void Dispose(bool disposing) + { + if (disposing && _application != null) + _application.PropertyChanged -= ApplicationOnPropertyChanged; + + base.Dispose(disposing); + } + + protected void LoadApplication(Application application) + { + if (application == null) + throw new ArgumentNullException("application"); + + Application.Current = application; + _application = application; + + application.PropertyChanged += ApplicationOnPropertyChanged; + } + + void ApplicationOnPropertyChanged(object sender, PropertyChangedEventArgs args) + { + if (args.PropertyName == "MainPage") + UpdateMainPage(); + } + + void SetMainPage() + { + UpdateMainPage(); + _window.MakeKeyAndVisible(); + } + + void UpdateMainPage() + { + if (_application.MainPage == null) + return; + + var platformRenderer = (PlatformRenderer)_window.RootViewController; + _window.RootViewController = _application.MainPage.CreateViewController(); + if (platformRenderer != null) + ((IDisposable)platformRenderer.Platform).Dispose(); + } + } +}
\ No newline at end of file diff --git a/Xamarin.Forms.Platform.iOS/GlobalCloseContextGestureRecognizer.cs b/Xamarin.Forms.Platform.iOS/GlobalCloseContextGestureRecognizer.cs new file mode 100644 index 00000000..fa76b982 --- /dev/null +++ b/Xamarin.Forms.Platform.iOS/GlobalCloseContextGestureRecognizer.cs @@ -0,0 +1,72 @@ +using System.Collections.Generic; +using System.Drawing; +#if __UNIFIED__ +using Foundation; +using UIKit; +#else +using MonoTouch.Foundation; +using MonoTouch.UIKit; +#endif +#if __UNIFIED__ +using RectangleF = CoreGraphics.CGRect; +using SizeF = CoreGraphics.CGSize; +using PointF = CoreGraphics.CGPoint; +using NSAction = System.Action; + +#else +using nfloat=System.Single; +using nint=System.Int32; +#endif + +namespace Xamarin.Forms.Platform.iOS +{ + internal class GlobalCloseContextGestureRecognizer : UIGestureRecognizer + { + List<UIButton> _buttons; + UIScrollView _scrollView; + + public GlobalCloseContextGestureRecognizer(UIScrollView scrollView, List<UIButton> buttons, NSAction activated) : base(activated) + { + _scrollView = scrollView; + _buttons = buttons; + + ShouldReceiveTouch = OnShouldReceiveTouch; + } + + public override void TouchesBegan(NSSet touches, UIEvent evt) + { + State = UIGestureRecognizerState.Began; + base.TouchesBegan(touches, evt); + } + + public override void TouchesEnded(NSSet touches, UIEvent evt) + { + State = UIGestureRecognizerState.Recognized; + base.TouchesEnded(touches, evt); + } + + public override void TouchesMoved(NSSet touches, UIEvent evt) + { + State = UIGestureRecognizerState.Recognized; + base.TouchesMoved(touches, evt); + } + + protected override void Dispose(bool disposing) + { + base.Dispose(disposing); + + if (disposing) + { + _buttons = null; + _scrollView = null; + } + } + + bool OnShouldReceiveTouch(UIGestureRecognizer r, UITouch t) + { + var scrollPos = t.LocationInView(_scrollView); + var rect = new RectangleF(0, 0, _scrollView.ContentSize.Width, _scrollView.ContentSize.Height); + return !rect.Contains(scrollPos); + } + } +}
\ No newline at end of file diff --git a/Xamarin.Forms.Platform.iOS/IVisualElementRenderer.cs b/Xamarin.Forms.Platform.iOS/IVisualElementRenderer.cs new file mode 100644 index 00000000..5ffabf08 --- /dev/null +++ b/Xamarin.Forms.Platform.iOS/IVisualElementRenderer.cs @@ -0,0 +1,27 @@ +using System; +#if __UNIFIED__ +using UIKit; + +#else +using MonoTouch.UIKit; +#endif + +namespace Xamarin.Forms.Platform.iOS +{ + public interface IVisualElementRenderer : IDisposable, IRegisterable + { + VisualElement Element { get; } + + UIView NativeView { get; } + + UIViewController ViewController { get; } + + event EventHandler<VisualElementChangedEventArgs> ElementChanged; + + SizeRequest GetDesiredSize(double widthConstraint, double heightConstraint); + + void SetElement(VisualElement element); + + void SetElementSize(Size size); + } +}
\ No newline at end of file diff --git a/Xamarin.Forms.Platform.iOS/LayoutExtensions.cs b/Xamarin.Forms.Platform.iOS/LayoutExtensions.cs new file mode 100644 index 00000000..6e7f5738 --- /dev/null +++ b/Xamarin.Forms.Platform.iOS/LayoutExtensions.cs @@ -0,0 +1,36 @@ +using System.Collections.Generic; +#if __UNIFIED__ +using CoreGraphics; +using UIKit; + +#else +using MonoTouch.UIKit; +#endif + +#if !__UNIFIED__ + // Save ourselves a ton of ugly ifdefs below +using CGSize = System.Drawing.SizeF; +#endif + +namespace Xamarin.Forms.Platform.iOS +{ + public delegate SizeRequest? GetDesiredSizeDelegate(NativeViewWrapperRenderer renderer, double widthConstraint, double heightConstraint); + + public delegate CGSize? SizeThatFitsDelegate(CGSize size); + + public delegate bool LayoutSubviewsDelegate(); + + public static class LayoutExtensions + { + public static void Add(this IList<View> children, UIView view, GetDesiredSizeDelegate getDesiredSizeDelegate = null, SizeThatFitsDelegate sizeThatFitsDelegate = null, + LayoutSubviewsDelegate layoutSubViews = null) + { + children.Add(view.ToView(getDesiredSizeDelegate, sizeThatFitsDelegate, layoutSubViews)); + } + + public static View ToView(this UIView view, GetDesiredSizeDelegate getDesiredSizeDelegate = null, SizeThatFitsDelegate sizeThatFitsDelegate = null, LayoutSubviewsDelegate layoutSubViews = null) + { + return new NativeViewWrapper(view, getDesiredSizeDelegate, sizeThatFitsDelegate, layoutSubViews); + } + } +}
\ No newline at end of file diff --git a/Xamarin.Forms.Platform.iOS/NativeViewWrapper.cs b/Xamarin.Forms.Platform.iOS/NativeViewWrapper.cs new file mode 100644 index 00000000..209aca80 --- /dev/null +++ b/Xamarin.Forms.Platform.iOS/NativeViewWrapper.cs @@ -0,0 +1,35 @@ +using System.Collections.Generic; +#if __UNIFIED__ +using CoreGraphics; +using UIKit; + +#else +using MonoTouch.UIKit; +#endif + +#if !__UNIFIED__ + // Save ourselves a ton of ugly ifdefs below +using CGSize = System.Drawing.SizeF; +#endif + +namespace Xamarin.Forms.Platform.iOS +{ + public class NativeViewWrapper : View + { + public NativeViewWrapper(UIView nativeView, GetDesiredSizeDelegate getDesiredSizeDelegate = null, SizeThatFitsDelegate sizeThatFitsDelegate = null, LayoutSubviewsDelegate layoutSubViews = null) + { + GetDesiredSizeDelegate = getDesiredSizeDelegate; + SizeThatFitsDelegate = sizeThatFitsDelegate; + LayoutSubViews = layoutSubViews; + NativeView = nativeView; + } + + public GetDesiredSizeDelegate GetDesiredSizeDelegate { get; } + + public LayoutSubviewsDelegate LayoutSubViews { get; set; } + + public UIView NativeView { get; } + + public SizeThatFitsDelegate SizeThatFitsDelegate { get; set; } + } +}
\ No newline at end of file diff --git a/Xamarin.Forms.Platform.iOS/NativeViewWrapperRenderer.cs b/Xamarin.Forms.Platform.iOS/NativeViewWrapperRenderer.cs new file mode 100644 index 00000000..3d3e6868 --- /dev/null +++ b/Xamarin.Forms.Platform.iOS/NativeViewWrapperRenderer.cs @@ -0,0 +1,72 @@ +using System.Collections.Generic; +#if __UNIFIED__ +using CoreGraphics; +using UIKit; + +#else +using MonoTouch.UIKit; +#endif + +#if !__UNIFIED__ + // Save ourselves a ton of ugly ifdefs below +using CGSize = System.Drawing.SizeF; +#endif + +namespace Xamarin.Forms.Platform.iOS +{ + public class NativeViewWrapperRenderer : ViewRenderer<NativeViewWrapper, UIView> + { + public override SizeRequest GetDesiredSize(double widthConstraint, double heightConstraint) + { + if (Element?.GetDesiredSizeDelegate == null) + return base.GetDesiredSize(widthConstraint, heightConstraint); + + // The user has specified a different implementation of GetDesiredSize + var result = Element.GetDesiredSizeDelegate(this, widthConstraint, heightConstraint); + + // If the GetDesiredSize delegate returns a SizeRequest, we use it; if it returns null, + // fall back to the default implementation + return result ?? base.GetDesiredSize(widthConstraint, heightConstraint); + } + + public override void LayoutSubviews() + { + if (Element?.LayoutSubViews == null) + { + Element?.InvalidateMeasure(InvalidationTrigger.MeasureChanged); + base.LayoutSubviews(); + return; + } + + // The user has specified a different implementation of LayoutSubviews + var handled = Element.LayoutSubViews(); + + if (!handled) + { + // If the delegate wasn't able to handle the request, fall back to the default implementation + base.LayoutSubviews(); + } + } + + public override CGSize SizeThatFits(CGSize size) + { + if (Element?.SizeThatFitsDelegate == null) + return base.SizeThatFits(size); + + // The user has specified a different implementation of SizeThatFits + var result = Element.SizeThatFitsDelegate(size); + + // If the delegate returns a value, we use it; + // if it returns null, fall back to the default implementation + return result ?? base.SizeThatFits(size); + } + + protected override void OnElementChanged(ElementChangedEventArgs<NativeViewWrapper> e) + { + base.OnElementChanged(e); + + if (e.OldElement == null) + SetNativeControl(Element.NativeView); + } + } +}
\ No newline at end of file diff --git a/Xamarin.Forms.Platform.iOS/PageExtensions.cs b/Xamarin.Forms.Platform.iOS/PageExtensions.cs new file mode 100644 index 00000000..07da048a --- /dev/null +++ b/Xamarin.Forms.Platform.iOS/PageExtensions.cs @@ -0,0 +1,33 @@ +using System; +#if __UNIFIED__ +using UIKit; + +#else +using MonoTouch.UIKit; +#endif + +namespace Xamarin.Forms +{ + public static class PageExtensions + { + public static UIViewController CreateViewController(this Page view) + { + if (!Forms.IsInitialized) + throw new InvalidOperationException("call Forms.Init() before this"); + + if (!(view.RealParent is Application)) + { + Application app = new DefaultApplication(); + app.MainPage = view; + } + + var result = new Platform.iOS.Platform(); + result.SetPage(view); + return result.ViewController; + } + + class DefaultApplication : Application + { + } + } +}
\ No newline at end of file diff --git a/Xamarin.Forms.Platform.iOS/Platform.cs b/Xamarin.Forms.Platform.iOS/Platform.cs new file mode 100644 index 00000000..0293e75f --- /dev/null +++ b/Xamarin.Forms.Platform.iOS/Platform.cs @@ -0,0 +1,505 @@ +using System; +using System.Collections.Generic; +using System.ComponentModel; +using System.Drawing; +using System.Linq; +using System.Runtime.InteropServices; +using System.Threading.Tasks; +#if __UNIFIED__ +using CoreGraphics; +using Foundation; +using ObjCRuntime; +using UIKit; +#else +using MonoTouch; +using MonoTouch.CoreAnimation; +using MonoTouch.CoreFoundation; +using MonoTouch.CoreGraphics; +using MonoTouch.UIKit; +using System.Drawing; +using MonoTouch.CoreAnimation; +using MonoTouch.Foundation; +using MonoTouch.ObjCRuntime; +using MonoTouch.UIKit; +#endif +#if __UNIFIED__ +using RectangleF = CoreGraphics.CGRect; +using SizeF = CoreGraphics.CGSize; +using PointF = CoreGraphics.CGPoint; + +#else +using nfloat=System.Single; +using nint=System.Int32; +using nuint=System.UInt32; +#endif + +namespace Xamarin.Forms.Platform.iOS +{ + public class Platform : BindableObject, IPlatform, INavigation, IDisposable + { + internal static readonly BindableProperty RendererProperty = BindableProperty.CreateAttached("Renderer", typeof(IVisualElementRenderer), typeof(Platform), default(IVisualElementRenderer), + propertyChanged: (bindable, oldvalue, newvalue) => + { + var view = bindable as VisualElement; + if (view != null) + view.IsPlatformEnabled = newvalue != null; + }); + + readonly int _alertPadding = 10; + + readonly List<Page> _modals; + readonly PlatformRenderer _renderer; + bool _animateModals = true; + bool _appeared; + + bool _disposed; + + internal Platform() + { + _renderer = new PlatformRenderer(this); + _modals = new List<Page>(); + + var busyCount = 0; + MessagingCenter.Subscribe(this, Page.BusySetSignalName, (Page sender, bool enabled) => + { + if (!PageIsChildOfPlatform(sender)) + return; + busyCount = Math.Max(0, enabled ? busyCount + 1 : busyCount - 1); + UIApplication.SharedApplication.NetworkActivityIndicatorVisible = busyCount > 0; + }); + + MessagingCenter.Subscribe(this, Page.AlertSignalName, (Page sender, AlertArguments arguments) => + { + if (!PageIsChildOfPlatform(sender)) + return; + + if (Forms.IsiOS8OrNewer) + { + var alert = UIAlertController.Create(arguments.Title, arguments.Message, UIAlertControllerStyle.Alert); + var oldFrame = alert.View.Frame; + alert.View.Frame = new RectangleF(oldFrame.X, oldFrame.Y, oldFrame.Width, oldFrame.Height - _alertPadding * 2); + alert.AddAction(UIAlertAction.Create(arguments.Cancel, UIAlertActionStyle.Cancel, a => arguments.SetResult(false))); + if (arguments.Accept != null) + alert.AddAction(UIAlertAction.Create(arguments.Accept, UIAlertActionStyle.Default, a => arguments.SetResult(true))); + var page = _modals.Any() ? _modals.Last() : Page; + var vc = GetRenderer(page).ViewController; + vc.PresentViewController(alert, true, null); + } + else + { + UIAlertView alertView; + if (arguments.Accept != null) + alertView = new UIAlertView(arguments.Title, arguments.Message, null, arguments.Cancel, arguments.Accept); + else + alertView = new UIAlertView(arguments.Title, arguments.Message, null, arguments.Cancel); + + alertView.Dismissed += (o, args) => arguments.SetResult(args.ButtonIndex != 0); + alertView.Show(); + } + }); + + MessagingCenter.Subscribe(this, Page.ActionSheetSignalName, (Page sender, ActionSheetArguments arguments) => + { + if (!PageIsChildOfPlatform(sender)) + return; + + var pageRoot = sender; + while (!Application.IsApplicationOrNull(pageRoot.RealParent)) + pageRoot = (Page)pageRoot.RealParent; + var pageRenderer = GetRenderer(pageRoot); + + if (Forms.IsiOS8OrNewer) + { + var alert = UIAlertController.Create(arguments.Title, null, UIAlertControllerStyle.ActionSheet); + + if (arguments.Cancel != null) + { + alert.AddAction(UIAlertAction.Create(arguments.Cancel, UIAlertActionStyle.Cancel, a => arguments.SetResult(arguments.Cancel))); + } + + if (arguments.Destruction != null) + { + alert.AddAction(UIAlertAction.Create(arguments.Destruction, UIAlertActionStyle.Destructive, a => arguments.SetResult(arguments.Destruction))); + } + + foreach (var label in arguments.Buttons) + { + if (label == null) + continue; + + var blabel = label; + alert.AddAction(UIAlertAction.Create(blabel, UIAlertActionStyle.Default, a => arguments.SetResult(blabel))); + } + + if (UIDevice.CurrentDevice.UserInterfaceIdiom == UIUserInterfaceIdiom.Pad) + { + UIDevice.CurrentDevice.BeginGeneratingDeviceOrientationNotifications(); + var observer = NSNotificationCenter.DefaultCenter.AddObserver(UIDevice.OrientationDidChangeNotification, + n => { alert.PopoverPresentationController.SourceRect = pageRenderer.ViewController.View.Bounds; }); + + arguments.Result.Task.ContinueWith(t => + { + NSNotificationCenter.DefaultCenter.RemoveObserver(observer); + UIDevice.CurrentDevice.EndGeneratingDeviceOrientationNotifications(); + }, TaskScheduler.FromCurrentSynchronizationContext()); + + alert.PopoverPresentationController.SourceView = pageRenderer.ViewController.View; + alert.PopoverPresentationController.SourceRect = pageRenderer.ViewController.View.Bounds; + alert.PopoverPresentationController.PermittedArrowDirections = 0; // No arrow + } + + pageRenderer.ViewController.PresentViewController(alert, true, null); + } + else + { + var actionSheet = new UIActionSheet(arguments.Title, null, arguments.Cancel, arguments.Destruction, arguments.Buttons.ToArray()); + + actionSheet.ShowInView(pageRenderer.NativeView); + + actionSheet.Clicked += (o, args) => + { + string title = null; + if (args.ButtonIndex != -1) + title = actionSheet.ButtonTitle(args.ButtonIndex); + + // iOS 8 always calls WillDismiss twice, once with the real result, and again with -1. + arguments.Result.TrySetResult(title); + }; + } + }); + } + + internal UIViewController ViewController + { + get { return _renderer; } + } + + Page Page { get; set; } + + Application TargetApplication + { + get + { + if (Page == null) + return null; + return Page.RealParent as Application; + } + } + + void IDisposable.Dispose() + { + if (_disposed) + return; + _disposed = true; + + Page.DescendantRemoved -= HandleChildRemoved; + MessagingCenter.Unsubscribe<Page, ActionSheetArguments>(this, Page.ActionSheetSignalName); + MessagingCenter.Unsubscribe<Page, AlertArguments>(this, Page.AlertSignalName); + MessagingCenter.Unsubscribe<Page, bool>(this, Page.BusySetSignalName); + + DisposeModelAndChildrenRenderers(Page); + foreach (var modal in _modals) + DisposeModelAndChildrenRenderers(modal); + + _renderer.Dispose(); + } + + void INavigation.InsertPageBefore(Page page, Page before) + { + throw new InvalidOperationException("InsertPageBefore is not supported globally on iOS, please use a NavigationPage."); + } + + IReadOnlyList<Page> INavigation.ModalStack + { + get { return _modals; } + } + + IReadOnlyList<Page> INavigation.NavigationStack + { + get { return new List<Page>(); } + } + + Task<Page> INavigation.PopAsync() + { + return ((INavigation)this).PopAsync(true); + } + + Task<Page> INavigation.PopAsync(bool animated) + { + throw new InvalidOperationException("PopAsync is not supported globally on iOS, please use a NavigationPage."); + } + + Task<Page> INavigation.PopModalAsync() + { + return ((INavigation)this).PopModalAsync(true); + } + + async Task<Page> INavigation.PopModalAsync(bool animated) + { + var modal = _modals.Last(); + _modals.Remove(modal); + modal.DescendantRemoved -= HandleChildRemoved; + + var controller = GetRenderer(modal) as UIViewController; + + if (_modals.Count >= 1 && controller != null) + await controller.DismissViewControllerAsync(animated); + else + await _renderer.DismissViewControllerAsync(animated); + + DisposeModelAndChildrenRenderers(modal); + + return modal; + } + + Task INavigation.PopToRootAsync() + { + return ((INavigation)this).PopToRootAsync(true); + } + + Task INavigation.PopToRootAsync(bool animated) + { + throw new InvalidOperationException("PopToRootAsync is not supported globally on iOS, please use a NavigationPage."); + } + + Task INavigation.PushAsync(Page root) + { + return ((INavigation)this).PushAsync(root, true); + } + + Task INavigation.PushAsync(Page root, bool animated) + { + throw new InvalidOperationException("PushAsync is not supported globally on iOS, please use a NavigationPage."); + } + + Task INavigation.PushModalAsync(Page modal) + { + return ((INavigation)this).PushModalAsync(modal, true); + } + + Task INavigation.PushModalAsync(Page modal, bool animated) + { + _modals.Add(modal); + modal.Platform = this; + + modal.DescendantRemoved += HandleChildRemoved; + + if (_appeared) + return PresentModal(modal, _animateModals && animated); + return Task.FromResult<object>(null); + } + + void INavigation.RemovePage(Page page) + { + throw new InvalidOperationException("RemovePage is not supported globally on iOS, please use a NavigationPage."); + } + + SizeRequest IPlatform.GetNativeSize(VisualElement view, double widthConstraint, double heightConstraint) + { + var renderView = GetRenderer(view); + if (renderView == null || renderView.NativeView == null) + return new SizeRequest(Size.Zero); + + return renderView.GetDesiredSize(widthConstraint, heightConstraint); + } + + public static IVisualElementRenderer CreateRenderer(VisualElement element) + { + var t = element.GetType(); + var renderer = Registrar.Registered.GetHandler<IVisualElementRenderer>(t) ?? new DefaultRenderer(); + renderer.SetElement(element); + return renderer; + } + + public static IVisualElementRenderer GetRenderer(VisualElement bindable) + { + return (IVisualElementRenderer)bindable.GetValue(RendererProperty); + } + + public static void SetRenderer(VisualElement bindable, IVisualElementRenderer value) + { + bindable.SetValue(RendererProperty, value); + } + + protected override void OnBindingContextChanged() + { + SetInheritedBindingContext(Page, BindingContext); + + base.OnBindingContextChanged(); + } + + internal void DidAppear() + { + _animateModals = false; + TargetApplication.NavigationProxy.Inner = this; + _animateModals = true; + } + + internal void DisposeModelAndChildrenRenderers(Element view) + { + IVisualElementRenderer renderer; + foreach (VisualElement child in view.Descendants()) + { + renderer = GetRenderer(child); + child.ClearValue(RendererProperty); + + if (renderer != null) + { + renderer.NativeView.RemoveFromSuperview(); + renderer.Dispose(); + } + } + + renderer = GetRenderer((VisualElement)view); + if (renderer != null) + { + if (renderer.ViewController != null) + { + var modalWrapper = renderer.ViewController.ParentViewController as ModalWrapper; + if (modalWrapper != null) + modalWrapper.Dispose(); + } + + renderer.NativeView.RemoveFromSuperview(); + renderer.Dispose(); + } + view.ClearValue(RendererProperty); + } + + internal void DisposeRendererAndChildren(IVisualElementRenderer rendererToRemove) + { + if (rendererToRemove == null) + return; + + if (rendererToRemove.Element != null && GetRenderer(rendererToRemove.Element) == rendererToRemove) + rendererToRemove.Element.ClearValue(RendererProperty); + + var subviews = rendererToRemove.NativeView.Subviews; + for (var i = 0; i < subviews.Length; i++) + { + var childRenderer = subviews[i] as IVisualElementRenderer; + if (childRenderer != null) + DisposeRendererAndChildren(childRenderer); + } + + rendererToRemove.NativeView.RemoveFromSuperview(); + rendererToRemove.Dispose(); + } + + internal void LayoutSubviews() + { + if (Page == null) + return; + + var rootRenderer = GetRenderer(Page); + + if (rootRenderer == null) + return; + + rootRenderer.SetElementSize(new Size(_renderer.View.Bounds.Width, _renderer.View.Bounds.Height)); + } + + internal void SetPage(Page newRoot) + { + if (newRoot == null) + return; + if (Page != null) + throw new NotImplementedException(); + Page = newRoot; + + if (_appeared == false) + return; + + Page.Platform = this; + AddChild(Page); + + Page.DescendantRemoved += HandleChildRemoved; + + TargetApplication.NavigationProxy.Inner = this; + } + + internal void WillAppear() + { + if (_appeared) + return; + + _renderer.View.BackgroundColor = UIColor.White; + _renderer.View.ContentMode = UIViewContentMode.Redraw; + + Page.Platform = this; + AddChild(Page); + + Page.DescendantRemoved += HandleChildRemoved; + + _appeared = true; + } + + void AddChild(VisualElement view) + { + if (!Application.IsApplicationOrNull(view.RealParent)) + Console.Error.WriteLine("Tried to add parented view to canvas directly"); + + if (GetRenderer(view) == null) + { + var viewRenderer = CreateRenderer(view); + SetRenderer(view, viewRenderer); + + _renderer.View.AddSubview(viewRenderer.NativeView); + if (viewRenderer.ViewController != null) + _renderer.AddChildViewController(viewRenderer.ViewController); + viewRenderer.NativeView.Frame = new RectangleF(0, 0, _renderer.View.Bounds.Width, _renderer.View.Bounds.Height); + viewRenderer.SetElementSize(new Size(_renderer.View.Bounds.Width, _renderer.View.Bounds.Height)); + } + else + Console.Error.WriteLine("Potential view double add"); + } + + void HandleChildRemoved(object sender, ElementEventArgs e) + { + var view = e.Element; + DisposeModelAndChildrenRenderers(view); + } + + bool PageIsChildOfPlatform(Page page) + { + while (!Application.IsApplicationOrNull(page.RealParent)) + page = (Page)page.RealParent; + + return Page == page || _modals.Contains(page); + } + + async Task PresentModal(Page modal, bool animated) + { + var modalRenderer = GetRenderer(modal); + if (modalRenderer == null) + { + modalRenderer = CreateRenderer(modal); + SetRenderer(modal, modalRenderer); + } + + var wrapper = new ModalWrapper(modalRenderer); + + if (_modals.Count > 1) + { + var topPage = _modals[_modals.Count - 2]; + var controller = GetRenderer(topPage) as UIViewController; + if (controller != null) + { + await controller.PresentViewControllerAsync(wrapper, animated); + await Task.Delay(5); + return; + } + } + + // One might wonder why these delays are here... well thats a great question. It turns out iOS will claim the + // presentation is complete before it really is. It does not however inform you when it is really done (and thus + // would be safe to dismiss the VC). Fortunately this is almost never an issue + await _renderer.PresentViewControllerAsync(wrapper, animated); + await Task.Delay(5); + } + + internal class DefaultRenderer : VisualElementRenderer<VisualElement> + { + } + } +}
\ No newline at end of file diff --git a/Xamarin.Forms.Platform.iOS/PlatformEffect.cs b/Xamarin.Forms.Platform.iOS/PlatformEffect.cs new file mode 100644 index 00000000..436ef6d7 --- /dev/null +++ b/Xamarin.Forms.Platform.iOS/PlatformEffect.cs @@ -0,0 +1,14 @@ +using System.ComponentModel; +#if __UNIFIED__ +using UIKit; + +#else +using MonoTouch.UIKit; +#endif + +namespace Xamarin.Forms.Platform.iOS +{ + public abstract class PlatformEffect : PlatformEffect<UIView, UIView> + { + } +}
\ No newline at end of file diff --git a/Xamarin.Forms.Platform.iOS/PlatformRenderer.cs b/Xamarin.Forms.Platform.iOS/PlatformRenderer.cs new file mode 100644 index 00000000..43534b2b --- /dev/null +++ b/Xamarin.Forms.Platform.iOS/PlatformRenderer.cs @@ -0,0 +1,85 @@ +using System; +#if __UNIFIED__ +using UIKit; + +#else +using MonoTouch.UIKit; +#endif + +namespace Xamarin.Forms.Platform.iOS +{ + internal class ModalWrapper : UIViewController + { + IVisualElementRenderer _modal; + + internal ModalWrapper(IVisualElementRenderer modal) + { + _modal = modal; + + View.BackgroundColor = UIColor.White; + View.AddSubview(modal.ViewController.View); + AddChildViewController(modal.ViewController); + + modal.ViewController.DidMoveToParentViewController(this); + } + + public override UIInterfaceOrientationMask GetSupportedInterfaceOrientations() + { + return UIInterfaceOrientationMask.All; + } + + public override void ViewDidLayoutSubviews() + { + base.ViewDidLayoutSubviews(); + if (_modal != null) + _modal.SetElementSize(new Size(View.Bounds.Width, View.Bounds.Height)); + } + + public override void ViewWillAppear(bool animated) + { + View.BackgroundColor = UIColor.White; + base.ViewWillAppear(animated); + } + + protected override void Dispose(bool disposing) + { + if (disposing) + _modal = null; + base.Dispose(disposing); + } + } + + internal class PlatformRenderer : UIViewController + { + internal PlatformRenderer(Platform platform) + { + Platform = platform; + } + + public Platform Platform { get; set; } + + public override UIInterfaceOrientationMask GetSupportedInterfaceOrientations() + { + return UIInterfaceOrientationMask.All; + } + + public override void ViewDidAppear(bool animated) + { + Platform.DidAppear(); + base.ViewDidAppear(animated); + } + + public override void ViewDidLayoutSubviews() + { + base.ViewDidLayoutSubviews(); + Platform.LayoutSubviews(); + } + + public override void ViewWillAppear(bool animated) + { + View.BackgroundColor = UIColor.White; + Platform.WillAppear(); + base.ViewWillAppear(animated); + } + } +}
\ No newline at end of file diff --git a/Xamarin.Forms.Platform.iOS/Properties/AssemblyInfo.cs b/Xamarin.Forms.Platform.iOS/Properties/AssemblyInfo.cs new file mode 100644 index 00000000..8ebe9ba1 --- /dev/null +++ b/Xamarin.Forms.Platform.iOS/Properties/AssemblyInfo.cs @@ -0,0 +1,69 @@ +using System.Reflection; +using System.Runtime.CompilerServices; +// Information about this assembly is defined by the following attributes. +// Change them to the values specific to your project. +using Xamarin.Forms; +using Xamarin.Forms.Platform.iOS; +#if __UNIFIED__ +using UIKit; + +#else +using MonoTouch.UIKit; +#endif + +[assembly: AssemblyTitle("Xamarin.Forms.Platform.iOS")] +[assembly: AssemblyDescription("iOS Backend for Xamarin.Forms")] +[assembly: AssemblyConfiguration("")] +[assembly: AssemblyCulture("")] + +// The following attributes are used to specify the signing key for the assembly, +// if desired. See the Mono documentation for more information about signing. + +//[assembly: AssemblyDelaySign(false)] +//[assembly: AssemblyKeyFile("")] + +[assembly: ExportRenderer(typeof(BoxView), typeof(BoxRenderer))] +[assembly: ExportRenderer(typeof(Entry), typeof(EntryRenderer))] +[assembly: ExportRenderer(typeof(Editor), typeof(EditorRenderer))] +[assembly: ExportRenderer(typeof(Label), typeof(LabelRenderer))] +[assembly: ExportRenderer(typeof(Image), typeof(ImageRenderer))] +[assembly: ExportRenderer(typeof(Button), typeof(ButtonRenderer))] +[assembly: ExportRenderer(typeof(TableView), typeof(TableViewRenderer))] +[assembly: ExportRenderer(typeof(Slider), typeof(SliderRenderer))] +[assembly: ExportRenderer(typeof(WebView), typeof(WebViewRenderer))] +[assembly: ExportRenderer(typeof(SearchBar), typeof(SearchBarRenderer))] +[assembly: ExportRenderer(typeof(Switch), typeof(SwitchRenderer))] +[assembly: ExportRenderer(typeof(DatePicker), typeof(DatePickerRenderer))] +[assembly: ExportRenderer(typeof(TimePicker), typeof(TimePickerRenderer))] +[assembly: ExportRenderer(typeof(Picker), typeof(PickerRenderer))] +[assembly: ExportRenderer(typeof(Stepper), typeof(StepperRenderer))] +[assembly: ExportRenderer(typeof(ProgressBar), typeof(ProgressBarRenderer))] +[assembly: ExportRenderer(typeof(ScrollView), typeof(ScrollViewRenderer))] +[assembly: ExportRenderer(typeof(Toolbar), typeof(ToolbarRenderer))] +[assembly: ExportRenderer(typeof(ActivityIndicator), typeof(ActivityIndicatorRenderer))] +[assembly: ExportRenderer(typeof(Frame), typeof(FrameRenderer))] +[assembly: ExportRenderer(typeof(NavigationMenu), typeof(NavigationMenuRenderer))] +[assembly: ExportRenderer(typeof(ListView), typeof(ListViewRenderer))] +[assembly: ExportRenderer(typeof(CarouselView), typeof(CarouselViewRenderer))] +[assembly: ExportRenderer(typeof(OpenGLView), typeof(OpenGLViewRenderer))] +[assembly: ExportRenderer(typeof(NativeViewWrapper), typeof(NativeViewWrapperRenderer))] +[assembly: ExportRenderer(typeof(TabbedPage), typeof(TabbedRenderer))] +[assembly: ExportRenderer(typeof(NavigationPage), typeof(NavigationRenderer))] +[assembly: ExportRenderer(typeof(CarouselPage), typeof(CarouselPageRenderer))] +[assembly: ExportRenderer(typeof(Page), typeof(PageRenderer))] +[assembly: ExportRenderer(typeof(MasterDetailPage), typeof(PhoneMasterDetailRenderer), UIUserInterfaceIdiom.Phone)] +[assembly: ExportRenderer(typeof(MasterDetailPage), typeof(TabletMasterDetailRenderer), UIUserInterfaceIdiom.Pad)] +[assembly: ExportCell(typeof(Cell), typeof(CellRenderer))] +[assembly: ExportCell(typeof(ImageCell), typeof(ImageCellRenderer))] +[assembly: ExportCell(typeof(EntryCell), typeof(EntryCellRenderer))] +[assembly: ExportCell(typeof(TextCell), typeof(TextCellRenderer))] +[assembly: ExportCell(typeof(ViewCell), typeof(ViewCellRenderer))] +[assembly: ExportCell(typeof(SwitchCell), typeof(SwitchCellRenderer))] +[assembly: ExportImageSourceHandler(typeof(FileImageSource), typeof(FileImageSourceHandler))] +[assembly: ExportImageSourceHandler(typeof(StreamImageSource), typeof(StreamImagesourceHandler))] +[assembly: ExportImageSourceHandler(typeof(UriImageSource), typeof(ImageLoaderSourceHandler))] +[assembly: InternalsVisibleTo("iOSUnitTests")] +[assembly: InternalsVisibleTo("Xamarin.Forms.Platform")] +[assembly: Xamarin.Forms.Dependency(typeof(Deserializer))] +[assembly: Xamarin.Forms.Dependency(typeof(ResourcesProvider))] +[assembly: Preserve]
\ No newline at end of file diff --git a/Xamarin.Forms.Platform.iOS/RendererFactory.cs b/Xamarin.Forms.Platform.iOS/RendererFactory.cs new file mode 100644 index 00000000..ff37905a --- /dev/null +++ b/Xamarin.Forms.Platform.iOS/RendererFactory.cs @@ -0,0 +1,13 @@ +using System; + +namespace Xamarin.Forms.Platform.iOS +{ + public static class RendererFactory + { + [Obsolete("Use Platform.CreateRenderer")] + public static IVisualElementRenderer GetRenderer(VisualElement view) + { + return Platform.CreateRenderer(view); + } + } +}
\ No newline at end of file diff --git a/Xamarin.Forms.Platform.iOS/RendererPool.cs b/Xamarin.Forms.Platform.iOS/RendererPool.cs new file mode 100644 index 00000000..25df358d --- /dev/null +++ b/Xamarin.Forms.Platform.iOS/RendererPool.cs @@ -0,0 +1,154 @@ +using System; +using System.Collections.Generic; +using System.Linq; +#if __UNIFIED__ +using UIKit; + +#else +using MonoTouch.UIKit; +#endif + +namespace Xamarin.Forms.Platform.iOS +{ + public sealed class RendererPool + { + readonly Dictionary<Type, Stack<IVisualElementRenderer>> _freeRenderers = new Dictionary<Type, Stack<IVisualElementRenderer>>(); + + readonly VisualElement _oldElement; + + readonly IVisualElementRenderer _parent; + + public RendererPool(IVisualElementRenderer renderer, VisualElement oldElement) + { + if (renderer == null) + throw new ArgumentNullException("renderer"); + + if (oldElement == null) + throw new ArgumentNullException("oldElement"); + + _oldElement = oldElement; + _parent = renderer; + } + + public IVisualElementRenderer GetFreeRenderer(VisualElement view) + { + if (view == null) + throw new ArgumentNullException("view"); + + var rendererType = Registrar.Registered.GetHandlerType(view.GetType()) ?? typeof(ViewRenderer); + + Stack<IVisualElementRenderer> renderers; + if (!_freeRenderers.TryGetValue(rendererType, out renderers) || renderers.Count == 0) + return null; + + var renderer = renderers.Pop(); + renderer.SetElement(view); + return renderer; + } + + public void UpdateNewElement(VisualElement newElement) + { + if (newElement == null) + throw new ArgumentNullException("newElement"); + + var sameChildrenTypes = true; + + var oldChildren = _oldElement.LogicalChildren; + var newChildren = newElement.LogicalChildren; + + if (oldChildren.Count == newChildren.Count) + { + for (var i = 0; i < oldChildren.Count; i++) + { + if (oldChildren[i].GetType() != newChildren[i].GetType()) + { + sameChildrenTypes = false; + break; + } + } + } + else + sameChildrenTypes = false; + + if (!sameChildrenTypes) + { + ClearRenderers(_parent); + FillChildrenWithRenderers(newElement); + } + else + UpdateRenderers(newElement); + } + + void ClearRenderers(IVisualElementRenderer renderer) + { + if (renderer == null) + return; + + var subviews = renderer.NativeView.Subviews; + for (var i = 0; i < subviews.Length; i++) + { + var childRenderer = subviews[i] as IVisualElementRenderer; + if (childRenderer != null) + { + PushRenderer(childRenderer); + + if (ReferenceEquals(childRenderer, Platform.GetRenderer(childRenderer.Element))) + childRenderer.Element.ClearValue(Platform.RendererProperty); + } + + subviews[i].RemoveFromSuperview(); + } + } + + void FillChildrenWithRenderers(VisualElement element) + { + foreach (var logicalChild in element.LogicalChildren) + { + var child = logicalChild as VisualElement; + if (child != null) + { + var renderer = GetFreeRenderer(child) ?? Platform.CreateRenderer(child); + Platform.SetRenderer(child, renderer); + + _parent.NativeView.AddSubview(renderer.NativeView); + } + } + } + + void PushRenderer(IVisualElementRenderer renderer) + { + var rendererType = renderer.GetType(); + + Stack<IVisualElementRenderer> renderers; + if (!_freeRenderers.TryGetValue(rendererType, out renderers)) + _freeRenderers[rendererType] = renderers = new Stack<IVisualElementRenderer>(); + + renderers.Push(renderer); + } + + void UpdateRenderers(Element newElement) + { + if (newElement.LogicalChildren.Count == 0) + return; + + var subviews = _parent.NativeView.Subviews; + for (var i = 0; i < subviews.Length; i++) + { + var childRenderer = subviews[i] as IVisualElementRenderer; + if (childRenderer == null) + continue; + + var x = (int)childRenderer.NativeView.Layer.ZPosition / 1000; + var element = newElement.LogicalChildren[x] as VisualElement; + if (element == null) + continue; + + if (childRenderer.Element != null && ReferenceEquals(childRenderer, Platform.GetRenderer(childRenderer.Element))) + childRenderer.Element.ClearValue(Platform.RendererProperty); + + childRenderer.SetElement(element); + Platform.SetRenderer(element, childRenderer); + } + } + } +}
\ No newline at end of file diff --git a/Xamarin.Forms.Platform.iOS/Renderers/ActivityIndicatorRenderer.cs b/Xamarin.Forms.Platform.iOS/Renderers/ActivityIndicatorRenderer.cs new file mode 100644 index 00000000..0ef8741f --- /dev/null +++ b/Xamarin.Forms.Platform.iOS/Renderers/ActivityIndicatorRenderer.cs @@ -0,0 +1,53 @@ +using System.Drawing; +using System.ComponentModel; +#if __UNIFIED__ +using UIKit; + +#else +using MonoTouch.UIKit; +#endif + +namespace Xamarin.Forms.Platform.iOS +{ + public class ActivityIndicatorRenderer : ViewRenderer<ActivityIndicator, UIActivityIndicatorView> + { + protected override void OnElementChanged(ElementChangedEventArgs<ActivityIndicator> e) + { + if (e.NewElement != null) + { + if (Control == null) + { + SetNativeControl(new UIActivityIndicatorView(RectangleF.Empty) { ActivityIndicatorViewStyle = UIActivityIndicatorViewStyle.Gray }); + } + + UpdateColor(); + UpdateIsRunning(); + } + + base.OnElementChanged(e); + } + + protected override void OnElementPropertyChanged(object sender, PropertyChangedEventArgs e) + { + base.OnElementPropertyChanged(sender, e); + + if (e.PropertyName == ActivityIndicator.ColorProperty.PropertyName) + UpdateColor(); + else if (e.PropertyName == ActivityIndicator.IsRunningProperty.PropertyName) + UpdateIsRunning(); + } + + void UpdateColor() + { + Control.Color = Element.Color == Color.Default ? null : Element.Color.ToUIColor(); + } + + void UpdateIsRunning() + { + if (Element.IsRunning) + Control.StartAnimating(); + else + Control.StopAnimating(); + } + } +}
\ No newline at end of file diff --git a/Xamarin.Forms.Platform.iOS/Renderers/AlignmentExtensions.cs b/Xamarin.Forms.Platform.iOS/Renderers/AlignmentExtensions.cs new file mode 100644 index 00000000..01d0bf66 --- /dev/null +++ b/Xamarin.Forms.Platform.iOS/Renderers/AlignmentExtensions.cs @@ -0,0 +1,25 @@ +#if __UNIFIED__ +using UIKit; + +#else +using MonoTouch.UIKit; +#endif + +namespace Xamarin.Forms.Platform.iOS +{ + internal static class AlignmentExtensions + { + internal static UITextAlignment ToNativeTextAlignment(this TextAlignment alignment) + { + switch (alignment) + { + case TextAlignment.Center: + return UITextAlignment.Center; + case TextAlignment.End: + return UITextAlignment.Right; + default: + return UITextAlignment.Left; + } + } + } +}
\ No newline at end of file diff --git a/Xamarin.Forms.Platform.iOS/Renderers/BoxRenderer.cs b/Xamarin.Forms.Platform.iOS/Renderers/BoxRenderer.cs new file mode 100644 index 00000000..f60ed6c1 --- /dev/null +++ b/Xamarin.Forms.Platform.iOS/Renderers/BoxRenderer.cs @@ -0,0 +1,78 @@ +using System.ComponentModel; +using System.Drawing; +#if __UNIFIED__ +using UIKit; +using CoreGraphics; +#else +using MonoTouch.UIKit; +using MonoTouch.CoreGraphics; +#endif +#if __UNIFIED__ +using RectangleF = CoreGraphics.CGRect; +using SizeF = CoreGraphics.CGSize; +using PointF = CoreGraphics.CGPoint; + +#else +using nfloat=System.Single; +using nint=System.Int32; +using nuint=System.UInt32; +#endif + +namespace Xamarin.Forms.Platform.iOS +{ + public class BoxRenderer : VisualElementRenderer<BoxView> + { + UIColor _colorToRenderer; + + SizeF _previousSize; + + public override void Draw(RectangleF rect) + { + using(var context = UIGraphics.GetCurrentContext()) + { + _colorToRenderer.SetFill(); + context.FillRect(rect); + } + base.Draw(rect); + + _previousSize = Bounds.Size; + } + + public override void LayoutSubviews() + { + if (_previousSize != Bounds.Size) + SetNeedsDisplay(); + } + + protected override void OnElementChanged(ElementChangedEventArgs<BoxView> e) + { + base.OnElementChanged(e); + + if (Element != null) + SetBackgroundColor(Element.BackgroundColor); + } + + protected override void OnElementPropertyChanged(object sender, PropertyChangedEventArgs e) + { + base.OnElementPropertyChanged(sender, e); + if (e.PropertyName == BoxView.ColorProperty.PropertyName) + SetBackgroundColor(Element.BackgroundColor); + else if (e.PropertyName == VisualElement.IsVisibleProperty.PropertyName && Element.IsVisible) + SetNeedsDisplay(); + } + + protected override void SetBackgroundColor(Color color) + { + if (Element == null) + return; + + var elementColor = Element.Color; + if (!elementColor.IsDefault) + _colorToRenderer = elementColor.ToUIColor(); + else + _colorToRenderer = color.ToUIColor(); + + SetNeedsDisplay(); + } + } +}
\ No newline at end of file diff --git a/Xamarin.Forms.Platform.iOS/Renderers/ButtonRenderer.cs b/Xamarin.Forms.Platform.iOS/Renderers/ButtonRenderer.cs new file mode 100644 index 00000000..3fa84d1d --- /dev/null +++ b/Xamarin.Forms.Platform.iOS/Renderers/ButtonRenderer.cs @@ -0,0 +1,190 @@ +using System; +using System.Drawing; +using System.Linq; +using System.ComponentModel; +#if __UNIFIED__ +using UIKit; +using CoreGraphics; +#else +using MonoTouch.UIKit; +using MonoTouch.CoreGraphics; +#endif +#if __UNIFIED__ +using RectangleF = CoreGraphics.CGRect; +using SizeF = CoreGraphics.CGSize; +using PointF = CoreGraphics.CGPoint; + +#else +using nfloat=System.Single; +using nint=System.Int32; +using nuint=System.UInt32; +#endif + +namespace Xamarin.Forms.Platform.iOS +{ + public class ButtonRenderer : ViewRenderer<Button, UIButton> + { + UIColor _buttonTextColorDefaultDisabled; + UIColor _buttonTextColorDefaultHighlighted; + UIColor _buttonTextColorDefaultNormal; + + public override SizeF SizeThatFits(SizeF size) + { + var result = base.SizeThatFits(size); + result.Height = 44; // Apple docs + //Compensate for the insets + if (!Control.ImageView.Hidden) + result.Width += 10; + return result; + } + + protected override void Dispose(bool disposing) + { + if (Control != null) + Control.TouchUpInside -= OnButtonTouchUpInside; + + base.Dispose(disposing); + } + + protected override void OnElementChanged(ElementChangedEventArgs<Button> e) + { + base.OnElementChanged(e); + + if (e.NewElement != null) + { + if (Control == null) + { + SetNativeControl(new UIButton(UIButtonType.RoundedRect)); + + _buttonTextColorDefaultNormal = Control.TitleColor(UIControlState.Normal); + _buttonTextColorDefaultHighlighted = Control.TitleColor(UIControlState.Highlighted); + _buttonTextColorDefaultDisabled = Control.TitleColor(UIControlState.Disabled); + + Control.TouchUpInside += OnButtonTouchUpInside; + } + + UpdateText(); + UpdateFont(); + UpdateBorder(); + UpdateImage(); + UpdateTextColor(); + } + } + + protected override void OnElementPropertyChanged(object sender, PropertyChangedEventArgs e) + { + base.OnElementPropertyChanged(sender, e); + + if (e.PropertyName == Button.TextProperty.PropertyName) + UpdateText(); + else if (e.PropertyName == Button.TextColorProperty.PropertyName) + UpdateTextColor(); + else if (e.PropertyName == Button.FontProperty.PropertyName) + UpdateFont(); + else if (e.PropertyName == Button.BorderWidthProperty.PropertyName || e.PropertyName == Button.BorderRadiusProperty.PropertyName || e.PropertyName == Button.BorderColorProperty.PropertyName) + UpdateBorder(); + else if (e.PropertyName == VisualElement.BackgroundColorProperty.PropertyName) + UpdateBackgroundVisibility(); + else if (e.PropertyName == Button.ImageProperty.PropertyName) + UpdateImage(); + } + + void OnButtonTouchUpInside(object sender, EventArgs eventArgs) + { + if (Element != null) + ((IButtonController)Element).SendClicked(); + } + + void UpdateBackgroundVisibility() + { + if (Forms.IsiOS7OrNewer) + return; + + var model = Element; + var shouldDrawImage = model.BackgroundColor == Color.Default; + + foreach (var control in Control.Subviews.Where(sv => !(sv is UILabel))) + control.Alpha = shouldDrawImage ? 1.0f : 0.0f; + } + + void UpdateBorder() + { + var uiButton = Control; + var button = Element; + + if (button.BorderColor != Color.Default) + uiButton.Layer.BorderColor = button.BorderColor.ToCGColor(); + + uiButton.Layer.BorderWidth = (float)button.BorderWidth; + uiButton.Layer.CornerRadius = button.BorderRadius; + + UpdateBackgroundVisibility(); + } + + void UpdateFont() + { + Control.TitleLabel.Font = Element.ToUIFont(); + } + + async void UpdateImage() + { + IImageSourceHandler handler; + var source = Element.Image; + if (source != null && (handler = Registrar.Registered.GetHandler<IImageSourceHandler>(source.GetType())) != null) + { + UIImage uiimage; + try + { + uiimage = await handler.LoadImageAsync(source, scale: (float)UIScreen.MainScreen.Scale); + } + catch (OperationCanceledException) + { + uiimage = null; + } + var button = Control; + if (button != null && uiimage != null) + { + if (Forms.IsiOS7OrNewer) + button.SetImage(uiimage.ImageWithRenderingMode(UIImageRenderingMode.AlwaysOriginal), UIControlState.Normal); + else + button.SetImage(uiimage, UIControlState.Normal); + button.ImageView.ContentMode = UIViewContentMode.ScaleAspectFit; + + Control.ImageEdgeInsets = new UIEdgeInsets(0, 0, 0, 10); + Control.TitleEdgeInsets = new UIEdgeInsets(0, 10, 0, 0); + } + } + else + { + Control.SetImage(null, UIControlState.Normal); + Control.ImageEdgeInsets = new UIEdgeInsets(0, 0, 0, 0); + Control.TitleEdgeInsets = new UIEdgeInsets(0, 0, 0, 0); + } + ((IVisualElementController)Element).NativeSizeChanged(); + } + + void UpdateText() + { + Control.SetTitle(Element.Text, UIControlState.Normal); + } + + void UpdateTextColor() + { + if (Element.TextColor == Color.Default) + { + Control.SetTitleColor(_buttonTextColorDefaultNormal, UIControlState.Normal); + Control.SetTitleColor(_buttonTextColorDefaultHighlighted, UIControlState.Highlighted); + Control.SetTitleColor(_buttonTextColorDefaultDisabled, UIControlState.Disabled); + } + else + { + Control.SetTitleColor(Element.TextColor.ToUIColor(), UIControlState.Normal); + Control.SetTitleColor(Element.TextColor.ToUIColor(), UIControlState.Highlighted); + Control.SetTitleColor(_buttonTextColorDefaultDisabled, UIControlState.Disabled); + + if (Forms.IsiOS7OrNewer) + Control.TintColor = Element.TextColor.ToUIColor(); + } + } + } +}
\ No newline at end of file diff --git a/Xamarin.Forms.Platform.iOS/Renderers/CarouselPageRenderer.cs b/Xamarin.Forms.Platform.iOS/Renderers/CarouselPageRenderer.cs new file mode 100644 index 00000000..16e97cd2 --- /dev/null +++ b/Xamarin.Forms.Platform.iOS/Renderers/CarouselPageRenderer.cs @@ -0,0 +1,395 @@ +using System; +using System.Collections.Generic; +using System.Collections.Specialized; +using System.ComponentModel; +using System.Drawing; +#if __UNIFIED__ +using UIKit; +#else +using MonoTouch.UIKit; +#endif +#if __UNIFIED__ +using RectangleF = CoreGraphics.CGRect; +using SizeF = CoreGraphics.CGSize; +using PointF = CoreGraphics.CGPoint; + +#else +using nfloat = System.Single; +using nint = System.Int32; +using nuint = System.UInt32; +#endif + +namespace Xamarin.Forms.Platform.iOS +{ + public class CarouselPageRenderer : UIViewController, IVisualElementRenderer + { + bool _appeared; + Dictionary<Page, UIView> _containerMap; + bool _disposed; + EventTracker _events; + bool _ignoreNativeScrolling; + UIScrollView _scrollView; + VisualElementTracker _tracker; + + public CarouselPageRenderer() + { + if (!Forms.IsiOS7OrNewer) + WantsFullScreenLayout = true; + } + + protected CarouselPage Carousel + { + get { return (CarouselPage)Element; } + } + + protected int SelectedIndex + { + get { return (int)(_scrollView.ContentOffset.X / _scrollView.Frame.Width); } + set { ScrollToPage(value); } + } + + public VisualElement Element { get; private set; } + + public event EventHandler<VisualElementChangedEventArgs> ElementChanged; + + public SizeRequest GetDesiredSize(double widthConstraint, double heightConstraint) + { + return NativeView.GetSizeRequest(widthConstraint, heightConstraint); + } + + public UIView NativeView + { + get { return View; } + } + + public void SetElement(VisualElement element) + { + VisualElement oldElement = Element; + Element = element; + _containerMap = new Dictionary<Page, UIView>(); + + OnElementChanged(new VisualElementChangedEventArgs(oldElement, element)); + + if (element != null) + element.SendViewInitialized(NativeView); + } + + public void SetElementSize(Size size) + { + Element.Layout(new Rectangle(Element.X, Element.Y, size.Width, size.Height)); + } + + public UIViewController ViewController + { + get { return this; } + } + + public override void DidRotate(UIInterfaceOrientation fromInterfaceOrientation) + { + _ignoreNativeScrolling = false; + View.SetNeedsLayout(); + } + + public override void ViewDidAppear(bool animated) + { + base.ViewDidAppear(animated); + + if (_appeared || _disposed) + return; + + _appeared = true; + Carousel.SendAppearing(); + } + + public override void ViewDidDisappear(bool animated) + { + base.ViewDidDisappear(animated); + + if (!_appeared || _disposed) + return; + + _appeared = false; + Carousel.SendDisappearing(); + } + + public override void ViewDidLayoutSubviews() + { + base.ViewDidLayoutSubviews(); + + View.Frame = View.Superview.Bounds; + _scrollView.Frame = View.Bounds; + + PositionChildren(); + UpdateCurrentPage(false); + } + + public override void ViewDidLoad() + { + base.ViewDidLoad(); + + _tracker = new VisualElementTracker(this); + _events = new EventTracker(this); + _events.LoadEvents(View); + + _scrollView = new UIScrollView { ShowsHorizontalScrollIndicator = false }; + + _scrollView.DecelerationEnded += OnDecelerationEnded; + + UpdateBackground(); + + View.Add(_scrollView); + + for (var i = 0; i < Element.LogicalChildren.Count; i++) + { + Element element = Element.LogicalChildren[i]; + var child = element as ContentPage; + if (child != null) + InsertPage(child, i); + } + + PositionChildren(); + + Carousel.PropertyChanged += OnPropertyChanged; + Carousel.PagesChanged += OnPagesChanged; + } + + public override void ViewDidUnload() + { + base.ViewDidUnload(); + + if (_scrollView != null) + _scrollView.DecelerationEnded -= OnDecelerationEnded; + + if (Carousel != null) + { + Carousel.PropertyChanged -= OnPropertyChanged; + Carousel.PagesChanged -= OnPagesChanged; + } + } + + public override void WillRotate(UIInterfaceOrientation toInterfaceOrientation, double duration) + { + _ignoreNativeScrolling = true; + } + + protected override void Dispose(bool disposing) + { + if (disposing && !_disposed) + { + if (_scrollView != null) + _scrollView.DecelerationEnded -= OnDecelerationEnded; + + if (Carousel != null) + { + Carousel.PropertyChanged -= OnPropertyChanged; + Carousel.PagesChanged -= OnPagesChanged; + } + + Platform.SetRenderer(Element, null); + + Clear(); + + if (_scrollView != null) + { + _scrollView.DecelerationEnded -= OnDecelerationEnded; + _scrollView.RemoveFromSuperview(); + _scrollView = null; + } + + if (_appeared) + { + _appeared = false; + Carousel.SendDisappearing(); + } + + if (_events != null) + { + _events.Dispose(); + _events = null; + } + + if (_tracker != null) + { + _tracker.Dispose(); + _tracker = null; + } + + Element = null; + _disposed = true; + } + + base.Dispose(disposing); + } + + protected virtual void OnElementChanged(VisualElementChangedEventArgs e) + { + EventHandler<VisualElementChangedEventArgs> changed = ElementChanged; + if (changed != null) + changed(this, e); + } + + void Clear() + { + foreach (KeyValuePair<Page, UIView> kvp in _containerMap) + { + kvp.Value.RemoveFromSuperview(); + IVisualElementRenderer renderer = Platform.GetRenderer(kvp.Key); + if (renderer != null) + { + renderer.ViewController.RemoveFromParentViewController(); + renderer.NativeView.RemoveFromSuperview(); + Platform.SetRenderer(kvp.Key, null); + } + } + _containerMap.Clear(); + } + + void InsertPage(ContentPage page, int index) + { + IVisualElementRenderer renderer = Platform.GetRenderer(page); + if (renderer == null) + { + renderer = Platform.CreateRenderer(page); + Platform.SetRenderer(page, renderer); + } + + UIView container = new PageContainer(page); + container.AddSubview(renderer.NativeView); + _containerMap[page] = container; + + AddChildViewController(renderer.ViewController); + _scrollView.InsertSubview(container, index); + + if ((index == 0 && SelectedIndex == 0) || (index < SelectedIndex)) + ScrollToPage(SelectedIndex + 1, false); + } + + void OnDecelerationEnded(object sender, EventArgs eventArgs) + { + if (_ignoreNativeScrolling || SelectedIndex >= Element.LogicalChildren.Count) + return; + + Carousel.CurrentPage = (ContentPage)Element.LogicalChildren[SelectedIndex]; + } + + void OnPagesChanged(object sender, NotifyCollectionChangedEventArgs e) + { + _ignoreNativeScrolling = true; + + NotifyCollectionChangedAction action = e.Apply((o, i, c) => InsertPage((ContentPage)o, i), (o, i) => RemovePage((ContentPage)o, i), Reset); + PositionChildren(); + + _ignoreNativeScrolling = false; + + if (action == NotifyCollectionChangedAction.Reset) + { + int index = Carousel.CurrentPage != null ? CarouselPage.GetIndex(Carousel.CurrentPage) : 0; + if (index < 0) + index = 0; + + ScrollToPage(index); + } + } + + void OnPropertyChanged(object sender, PropertyChangedEventArgs e) + { + if (e.PropertyName == "CurrentPage") + UpdateCurrentPage(); + else if (e.PropertyName == VisualElement.BackgroundColorProperty.PropertyName) + UpdateBackground(); + else if (e.PropertyName == Page.BackgroundImageProperty.PropertyName) + UpdateBackground(); + } + + void PositionChildren() + { + nfloat x = 0; + RectangleF bounds = View.Bounds; + foreach (ContentPage child in ((CarouselPage)Element).Children) + { + UIView container = _containerMap[child]; + + container.Frame = new RectangleF(x, bounds.Y, bounds.Width, bounds.Height); + x += bounds.Width; + } + + _scrollView.PagingEnabled = true; + _scrollView.ContentSize = new SizeF(bounds.Width * ((CarouselPage)Element).Children.Count, bounds.Height); + } + + void RemovePage(ContentPage page, int index) + { + UIView container = _containerMap[page]; + container.RemoveFromSuperview(); + _containerMap.Remove(page); + + IVisualElementRenderer renderer = Platform.GetRenderer(page); + if (renderer == null) + return; + + renderer.ViewController.RemoveFromParentViewController(); + renderer.NativeView.RemoveFromSuperview(); + } + + void Reset() + { + Clear(); + + for (var i = 0; i < Element.LogicalChildren.Count; i++) + { + Element element = Element.LogicalChildren[i]; + var child = element as ContentPage; + if (child != null) + InsertPage(child, i); + } + } + + void ScrollToPage(int index, bool animated = true) + { + if (_scrollView.ContentOffset.X == index * _scrollView.Frame.Width) + return; + + _scrollView.SetContentOffset(new PointF(index * _scrollView.Frame.Width, 0), animated); + } + + void UpdateBackground() + { + string bgImage = ((Page)Element).BackgroundImage; + if (!string.IsNullOrEmpty(bgImage)) + { + View.BackgroundColor = UIColor.FromPatternImage(UIImage.FromBundle(bgImage)); + return; + } + Color bgColor = Element.BackgroundColor; + if (bgColor.IsDefault) + View.BackgroundColor = UIColor.White; + else + View.BackgroundColor = bgColor.ToUIColor(); + } + + void UpdateCurrentPage(bool animated = true) + { + ContentPage current = Carousel.CurrentPage; + if (current != null) + ScrollToPage(CarouselPage.GetIndex(current), animated); + } + + class PageContainer : UIView + { + public PageContainer(VisualElement element) + { + Element = element; + } + + public VisualElement Element { get; } + + public override void LayoutSubviews() + { + base.LayoutSubviews(); + + if (Subviews.Length > 0) + Subviews[0].Frame = new RectangleF(0, 0, (float)Element.Width, (float)Element.Height); + } + } + } +}
\ No newline at end of file diff --git a/Xamarin.Forms.Platform.iOS/Renderers/CarouselViewRenderer.cs b/Xamarin.Forms.Platform.iOS/Renderers/CarouselViewRenderer.cs new file mode 100644 index 00000000..26ef16cc --- /dev/null +++ b/Xamarin.Forms.Platform.iOS/Renderers/CarouselViewRenderer.cs @@ -0,0 +1,454 @@ +using System; +using System.Collections; +using System.Collections.Generic; +using System.Collections.Specialized; +using System.ComponentModel; +using System.Drawing; +using System.Linq; +#if __UNIFIED__ +using UIKit; +using Foundation; +#else +using MonoTouch.UIKit; +using MonoTouch.Foundation; +#endif +#if __UNIFIED__ +using RectangleF = CoreGraphics.CGRect; +using SizeF = CoreGraphics.CGSize; +using PointF = CoreGraphics.CGPoint; + +#else +using nfloat=System.Single; +using nint=System.Int32; +using nuint=System.UInt32; +#endif + +namespace Xamarin.Forms.Platform.iOS +{ + /// <summary> + /// UICollectionView visualizes a collection of data. UICollectionViews are created indirectly by first creating a + /// CarouselViewController from which the CollectionView is accessed via the CollectionView property. + /// The CarouselViewController functionality is exposed through a set of interfaces (aka "conforms to" in the Apple + /// docs). + /// When Xamarin exposed CarouselViewRenderer the following interfaces where implemented as virtual methods: + /// UICollectionViewSource + /// UIScrollViewDelegate + /// UICollectionViewDelegate Allow you to manage the selection and highlighting of items in a collection view + /// UICollectionViewDataSource Creation and configuration of cells and supplementary views used to display data + /// The interfaces only implement required method while the UICollectionView exposes optional methods via + /// ExportAttribute. + /// The C# method name may be aliased. For example, C# "GetCell" maps to obj-C "CellForItemAtIndexPath". + /// <seealso cref="https://developer.apple.com/library/ios/documentation/UIKit/Reference/UICollectionView_class/" /> + /// </summary> + public class CarouselViewRenderer : ViewRenderer<CarouselView, UICollectionView> + { + const int DefaultMinimumDimension = 44; + static readonly UIColor DefaultBackgroundColor = UIColor.White; + + CarouselViewController.Layout _layout; + int _position; + + CarouselViewController CarouselViewController { get; set; } + + new UIScrollView Control + { + get + { + Initialize(); + return base.Control; + } + } + + ICarouselViewController Controller + { + get { return Element; } + } + + public override SizeRequest GetDesiredSize(double widthConstraint, double heightConstraint) + { + return Control.GetSizeRequest(widthConstraint, heightConstraint, DefaultMinimumDimension, DefaultMinimumDimension); + } + + protected override void OnElementChanged(ElementChangedEventArgs<CarouselView> e) + { + base.OnElementChanged(e); + Initialize(); + } + + protected override void OnElementPropertyChanged(object sender, PropertyChangedEventArgs e) + { + if (e.PropertyName == "Position") + // not ideal; the event is raised before the animation to move completes (or even starts) + ScrollToPosition(Element.Position); + + base.OnElementPropertyChanged(sender, e); + } + + void Initialize() + { + // cache hit? + var carouselView = base.Control; + if (carouselView != null) + return; + + CarouselViewController = new CarouselViewController(this, _layout = new CarouselViewController.Layout(UICollectionViewScrollDirection.Horizontal), Element.Position); + + // hook up on position changed event + // not ideal; the event is raised upon releasing the swipe instead of animation completion + _layout.OnSwipeOffsetChosen += o => OnPositionChange(o); + + // hook up crud events + Element.CollectionChanged += OnCollectionChanged; + + // populate cache + SetNativeControl(CarouselViewController.CollectionView); + } + + void OnCollectionChanged(object source, NotifyCollectionChangedEventArgs e) + { + switch (e.Action) + { + case NotifyCollectionChangedAction.Add: + CarouselViewController.ReloadData(); + + if (e.NewStartingIndex <= _position) + ShiftPosition(e.NewItems.Count); + + break; + + case NotifyCollectionChangedAction.Move: + for (var i = 0; i < e.NewItems.Count; i++) + { + CarouselViewController.MoveItem(e.OldStartingIndex + i, e.NewStartingIndex + i); + } + break; + + case NotifyCollectionChangedAction.Remove: + if (Element.Count == 0) + throw new InvalidOperationException("CarouselView must retain a least one item."); + + if (e.OldStartingIndex == _position) + { + CarouselViewController.DeleteItems(Enumerable.Range(e.OldStartingIndex, e.OldItems.Count)); + if (_position == Element.Count) + _position--; + OnItemChange(_position); + } + + else + { + CarouselViewController.ReloadData(); + + if (e.OldStartingIndex < _position) + ShiftPosition(-e.OldItems.Count); + } + + break; + + case NotifyCollectionChangedAction.Replace: + CarouselViewController.ReloadItems(Enumerable.Range(e.OldStartingIndex, e.OldItems.Count)); + break; + + case NotifyCollectionChangedAction.Reset: + CarouselViewController.ReloadData(); + break; + + default: + throw new Exception(); + } + } + + void OnItemChange(int position) + { + var item = Controller.GetItem(position); + Controller.SendSelectedItemChanged(item); + } + + bool OnPositionChange(int position) + { + if (position == _position) + return false; + + _position = position; + + Controller.SendSelectedPositionChanged(position); + OnItemChange(position); + return true; + } + + void ScrollToPosition(int position, bool animated = true) + { + if (!OnPositionChange(position)) + return; + + CarouselViewController.ScrollToPosition(position, animated); + } + + void ShiftPosition(int offset) + { + // By default the position remains the same which causes an animation in the case + // of the added/removed position preceding the current position. I prefer the constructed + // Android behavior whereby the item remains the same and the position changes. + ScrollToPosition(_position + offset, false); + } + } + + internal sealed class CarouselViewController : UICollectionViewController + { + readonly Dictionary<object, int> _typeIdByType; + UICollectionViewLayout _layout; + int _nextItemTypeId; + int _originPosition; + + public Action<int> OnBind; + public Action<int> OnSwipeTargetChosen; + + public CarouselViewController(CarouselViewRenderer renderer, UICollectionViewLayout layout, int originPosition) : base(layout) + { + Renderer = renderer; + _typeIdByType = new Dictionary<object, int>(); + _nextItemTypeId = 0; + _layout = layout; + _originPosition = originPosition; + } + + ICarouselViewController Controller + { + get { return Element; } + } + + CarouselView Element + { + get { return Renderer.Element; } + } + + CarouselViewRenderer Renderer { get; } + + public void DeleteItems(IEnumerable<int> positions) + { + var indices = positions.Select(o => NSIndexPath.FromRowSection(o, 0)).ToArray(); + CollectionView.DeleteItems(indices); + } + + public override UICollectionViewCell GetCell(UICollectionView collectionView, NSIndexPath indexPath) + { + var index = indexPath.Row; + + if (_originPosition != 0) + index = _originPosition; + + var item = Controller.GetItem(index); + var itemType = Controller.GetItemType(item); + + var itemTypeId = default(int); + if (!_typeIdByType.TryGetValue(itemType, out itemTypeId)) + { + _typeIdByType[itemType] = itemTypeId = _nextItemTypeId++; + CollectionView.RegisterClassForCell(typeof(Cell), itemTypeId.ToString()); + } + + var cell = (Cell)CollectionView.DequeueReusableCell(itemTypeId.ToString(), indexPath); + cell.Initialize(Element, itemType, item, index); + + // a semantically weak approach to OnAppearing; decided not to expose as such + if (cell.OnBind == null) + cell.OnBind += o => OnBind?.Invoke(o); + + return cell; + } + + public override nint GetItemsCount(UICollectionView collectionView, nint section) + { + var result = Element.Count; + return result; + } + + public void MoveItem(int oldPosition, int newPosition) + { + base.MoveItem(CollectionView, NSIndexPath.FromRowSection(oldPosition, 0), NSIndexPath.FromRowSection(newPosition, 0)); + } + + public override nint NumberOfSections(UICollectionView collectionView) + { + return 1; + } + + public void ReloadData() => CollectionView.ReloadData(); + + public void ReloadItems(IEnumerable<int> positions) + { + var indices = positions.Select(o => NSIndexPath.FromRowSection(o, 0)).ToArray(); + CollectionView.ReloadItems(indices); + } + + public void ScrollToPosition(int position, bool animated = true) + { + CollectionView.ScrollToItem(NSIndexPath.FromRowSection(position, 0), UICollectionViewScrollPosition.CenteredHorizontally, animated); + } + + public override void ViewDidLoad() + { + base.ViewDidLoad(); + + CollectionView.PagingEnabled = true; + CollectionView.BackgroundColor = UIColor.Clear; + } + + public override void WillDisplayCell(UICollectionView collectionView, UICollectionViewCell cell, NSIndexPath indexPath) + { + if (_originPosition == 0) + return; + + // Ideally position zero would not be rendered in memory however it is. + // Thankfully, position zero is not displyed; position originPosition is rendered and displayed. + ScrollToPosition(_originPosition, false); + _originPosition = 0; + } + + [Export("collectionView:layout:sizeForItemAtIndexPath:")] + SizeF GetSizeForItem(UICollectionView collectionView, UICollectionViewLayout layout, NSIndexPath indexPath) + { + return collectionView.Frame.Size; + } + + internal new sealed class Layout : UICollectionViewFlowLayout + { + static readonly nfloat ZeroMinimumInteritemSpacing = 0; + static readonly nfloat ZeroMinimumLineSpacing = 0; + + int _width; + + public Action<int> OnSwipeOffsetChosen; + + public Layout(UICollectionViewScrollDirection scrollDirection) + { + ScrollDirection = scrollDirection; + MinimumInteritemSpacing = ZeroMinimumInteritemSpacing; + MinimumLineSpacing = ZeroMinimumLineSpacing; + } + + public override SizeF CollectionViewContentSize + { + get + { + var result = base.CollectionViewContentSize; + return result; + } + } + + public override UICollectionViewLayoutAttributes FinalLayoutAttributesForDisappearingItem(NSIndexPath itemIndexPath) + { + return base.FinalLayoutAttributesForDisappearingItem(itemIndexPath); + } + + public override NSIndexPath GetTargetIndexPathForInteractivelyMovingItem(NSIndexPath previousIndexPath, PointF position) + { + var result = base.GetTargetIndexPathForInteractivelyMovingItem(previousIndexPath, position); + return result; + } + + public override UICollectionViewLayoutAttributes InitialLayoutAttributesForAppearingItem(NSIndexPath itemIndexPath) + { + return base.InitialLayoutAttributesForAppearingItem(itemIndexPath); + } + + public override UICollectionViewLayoutAttributes[] LayoutAttributesForElementsInRect(RectangleF rect) + { + // couldn't figure a way to use these values to compute when an element appeared to disappeared. YMMV + var result = base.LayoutAttributesForElementsInRect(rect); + foreach (var item in result) + { + var index = item.IndexPath; + var category = item.RepresentedElementCategory; + var kind = item.RepresentedElementKind; + + var hidden = item.Hidden; + var bounds = item.Bounds; + var frame = item.Frame; + var center = item.Center; + + _width = (int)item.Bounds.Width; + } + return result; + } + + public override bool ShouldInvalidateLayoutForBoundsChange(RectangleF newBounds) + { + return true; + } + + public override PointF TargetContentOffset(PointF proposedContentOffset, PointF scrollingVelocity) + { + var result = base.TargetContentOffset(proposedContentOffset, scrollingVelocity); + OnSwipeOffsetChosen?.Invoke((int)result.X / _width); + return result; + } + + public override PointF TargetContentOffsetForProposedContentOffset(PointF proposedContentOffset) + { + var result = base.TargetContentOffsetForProposedContentOffset(proposedContentOffset); + return result; + } + } + + sealed class Cell : UICollectionViewCell + { + IItemViewController _controller; + int _position; + IVisualElementRenderer _renderer; + View _view; + + public Action<int> OnBind; + + [Export("initWithFrame:")] + internal Cell(RectangleF frame) : base(frame) + { + _position = -1; + } + + public override void LayoutSubviews() + { + base.LayoutSubviews(); + + _renderer.Element.Layout(new Rectangle(0, 0, ContentView.Frame.Width, ContentView.Frame.Height)); + } + + internal void Initialize(IItemViewController controller, object itemType, object item, int position) + { + _position = position; + + if (_controller == null) + { + _controller = controller; + + // create view + _view = controller.CreateView(itemType); + + // bind view + Bind(item, position); + + // render view + _renderer = Platform.CreateRenderer(_view); + Platform.SetRenderer(_view, _renderer); + + // attach view + var uiView = _renderer.NativeView; + ContentView.AddSubview(uiView); + } + else + Bind(item, position); + } + + void Bind(object item, int position) + { + //if (position != this.position) + // controller.SendPositionDisappearing (this.position); + + _position = position; + OnBind?.Invoke(position); + + _controller.BindView(_view, item); + } + } + } +}
\ No newline at end of file diff --git a/Xamarin.Forms.Platform.iOS/Renderers/DatePickerRenderer.cs b/Xamarin.Forms.Platform.iOS/Renderers/DatePickerRenderer.cs new file mode 100644 index 00000000..fd2b1549 --- /dev/null +++ b/Xamarin.Forms.Platform.iOS/Renderers/DatePickerRenderer.cs @@ -0,0 +1,122 @@ +using System; +using System.Drawing; +using System.ComponentModel; +#if __UNIFIED__ +using UIKit; +using Foundation; +#else +using MonoTouch.UIKit; +using MonoTouch.Foundation; +#endif +#if __UNIFIED__ +using RectangleF = CoreGraphics.CGRect; +using SizeF = CoreGraphics.CGSize; +using PointF = CoreGraphics.CGPoint; + +#else +using nfloat=System.Single; +using nint=System.Int32; +using nuint=System.UInt32; +#endif + +namespace Xamarin.Forms.Platform.iOS +{ + internal class NoCaretField : UITextField + { + public NoCaretField() : base(new RectangleF()) + { + } + + public override RectangleF GetCaretRectForPosition(UITextPosition position) + { + return new RectangleF(); + } + } + + public class DatePickerRenderer : ViewRenderer<DatePicker, UITextField> + { + UIDatePicker _picker; + + protected override void OnElementChanged(ElementChangedEventArgs<DatePicker> e) + { + base.OnElementChanged(e); + + if (e.OldElement == null) + { + var entry = new NoCaretField { BorderStyle = UITextBorderStyle.RoundedRect }; + + entry.Started += OnStarted; + entry.Ended += OnEnded; + + _picker = new UIDatePicker { Mode = UIDatePickerMode.Date, TimeZone = new NSTimeZone("UTC") }; + + _picker.ValueChanged += HandleValueChanged; + + var width = UIScreen.MainScreen.Bounds.Width; + var toolbar = new UIToolbar(new RectangleF(0, 0, width, 44)) { BarStyle = UIBarStyle.Default, Translucent = true }; + var spacer = new UIBarButtonItem(UIBarButtonSystemItem.FlexibleSpace); + var doneButton = new UIBarButtonItem(UIBarButtonSystemItem.Done, (o, a) => entry.ResignFirstResponder()); + + toolbar.SetItems(new[] { spacer, doneButton }, false); + + entry.InputView = _picker; + entry.InputAccessoryView = toolbar; + + SetNativeControl(entry); + } + + if (e.NewElement != null) + { + UpdateDateFromModel(false); + UpdateMaximumDate(); + UpdateMinimumDate(); + } + } + + protected override void OnElementPropertyChanged(object sender, PropertyChangedEventArgs e) + { + base.OnElementPropertyChanged(sender, e); + + if (e.PropertyName == DatePicker.DateProperty.PropertyName || e.PropertyName == DatePicker.FormatProperty.PropertyName) + UpdateDateFromModel(true); + else if (e.PropertyName == DatePicker.MinimumDateProperty.PropertyName) + UpdateMinimumDate(); + else if (e.PropertyName == DatePicker.MaximumDateProperty.PropertyName) + UpdateMaximumDate(); + } + + void HandleValueChanged(object sender, EventArgs e) + { + if (Element != null) + ((IElementController)Element).SetValueFromRenderer(DatePicker.DateProperty, _picker.Date.ToDateTime().Date); + } + + void OnEnded(object sender, EventArgs eventArgs) + { + ((IElementController)Element).SetValueFromRenderer(VisualElement.IsFocusedPropertyKey, false); + } + + void OnStarted(object sender, EventArgs eventArgs) + { + ((IElementController)Element).SetValueFromRenderer(VisualElement.IsFocusedPropertyKey, true); + } + + void UpdateDateFromModel(bool animate) + { + if (_picker.Date.ToDateTime().Date != Element.Date.Date) + _picker.SetDate(Element.Date.ToNSDate(), animate); + + Control.Text = Element.Date.ToString(Element.Format); + } + + void UpdateMaximumDate() + { + _picker.MaximumDate = Element.MaximumDate.ToNSDate(); + } + + void UpdateMinimumDate() + { + _picker.MinimumDate = Element.MinimumDate.ToNSDate(); + } + } +}
\ No newline at end of file diff --git a/Xamarin.Forms.Platform.iOS/Renderers/EditorRenderer.cs b/Xamarin.Forms.Platform.iOS/Renderers/EditorRenderer.cs new file mode 100644 index 00000000..4d5a3f99 --- /dev/null +++ b/Xamarin.Forms.Platform.iOS/Renderers/EditorRenderer.cs @@ -0,0 +1,159 @@ +using System; +using System.Drawing; +using System.ComponentModel; +#if __UNIFIED__ +using UIKit; +#else +using MonoTouch.UIKit; +#endif +#if __UNIFIED__ +using RectangleF = CoreGraphics.CGRect; +using SizeF = CoreGraphics.CGSize; +using PointF = CoreGraphics.CGPoint; + +#else +using nfloat=System.Single; +using nint=System.Int32; +using nuint=System.UInt32; +#endif + +namespace Xamarin.Forms.Platform.iOS +{ + public class EditorRenderer : ViewRenderer<Editor, UITextView> + { + UIToolbar _accessoryView; + + public override SizeRequest GetDesiredSize(double widthConstraint, double heightConstraint) + { + if (!Forms.IsiOS7OrNewer) + { + // Avoid crash iOS 6. iOS 6, I hate you. Why you no like Infinite size? + return base.GetDesiredSize(Math.Min(widthConstraint, 2000), Math.Min(heightConstraint, 2000)); + } + return base.GetDesiredSize(widthConstraint, heightConstraint); + } + + protected override void Dispose(bool disposing) + { + if (disposing) + { + Control.Changed -= HandleChanged; + Control.Started -= OnStarted; + Control.Ended -= OnEnded; + } + + base.Dispose(disposing); + } + + protected override void OnElementChanged(ElementChangedEventArgs<Editor> e) + { + base.OnElementChanged(e); + + if (Control == null) + { + SetNativeControl(new UITextView(RectangleF.Empty)); + + if (Device.Idiom == TargetIdiom.Phone) + { + // iPhone does not have a dismiss keyboard button + var keyboardWidth = UIScreen.MainScreen.Bounds.Width; + _accessoryView = new UIToolbar(new RectangleF(0, 0, keyboardWidth, 44)) { BarStyle = UIBarStyle.Default, Translucent = true }; + + var spacer = new UIBarButtonItem(UIBarButtonSystemItem.FlexibleSpace); + var doneButton = new UIBarButtonItem(UIBarButtonSystemItem.Done, (o, a) => + { + Control.ResignFirstResponder(); + Element.SendCompleted(); + }); + _accessoryView.SetItems(new[] { spacer, doneButton }, false); + Control.InputAccessoryView = _accessoryView; + } + + Control.Changed += HandleChanged; + Control.Started += OnStarted; + Control.Ended += OnEnded; + } + + if (e.NewElement != null) + { + UpdateText(); + UpdateFont(); + UpdateTextColor(); + UpdateKeyboard(); + UpdateEditable(); + } + } + + protected override void OnElementPropertyChanged(object sender, PropertyChangedEventArgs e) + { + base.OnElementPropertyChanged(sender, e); + + if (e.PropertyName == Editor.TextProperty.PropertyName) + UpdateText(); + else if (e.PropertyName == Xamarin.Forms.InputView.KeyboardProperty.PropertyName) + UpdateKeyboard(); + else if (e.PropertyName == VisualElement.IsEnabledProperty.PropertyName) + UpdateEditable(); + else if (e.PropertyName == Editor.TextColorProperty.PropertyName) + UpdateTextColor(); + else if (e.PropertyName == Editor.FontAttributesProperty.PropertyName) + UpdateFont(); + else if (e.PropertyName == Editor.FontFamilyProperty.PropertyName) + UpdateFont(); + else if (e.PropertyName == Editor.FontSizeProperty.PropertyName) + UpdateFont(); + } + + void HandleChanged(object sender, EventArgs e) + { + ((IElementController)Element).SetValueFromRenderer(Editor.TextProperty, Control.Text); + } + + void OnEnded(object sender, EventArgs eventArgs) + { + Element.SetValue(VisualElement.IsFocusedPropertyKey, false); + Element.SendCompleted(); + } + + void OnStarted(object sender, EventArgs eventArgs) + { + ((IElementController)Element).SetValueFromRenderer(VisualElement.IsFocusedPropertyKey, true); + } + + void UpdateEditable() + { + Control.Editable = Element.IsEnabled; + Control.UserInteractionEnabled = Element.IsEnabled; + + if (Control.InputAccessoryView != null) + Control.InputAccessoryView.Hidden = !Element.IsEnabled; + } + + void UpdateFont() + { + Control.Font = Element.ToUIFont(); + } + + void UpdateKeyboard() + { + Control.ApplyKeyboard(Element.Keyboard); + } + + void UpdateText() + { + // ReSharper disable once RedundantCheckBeforeAssignment + if (Control.Text != Element.Text) + Control.Text = Element.Text; + } + + void UpdateTextColor() + { + var textColor = Element.TextColor; + + if (textColor.IsDefault) + Control.TextColor = UIColor.Black; + else + Control.TextColor = textColor.ToUIColor(); + } + } +}
\ No newline at end of file diff --git a/Xamarin.Forms.Platform.iOS/Renderers/EntryRenderer.cs b/Xamarin.Forms.Platform.iOS/Renderers/EntryRenderer.cs new file mode 100644 index 00000000..734a56be --- /dev/null +++ b/Xamarin.Forms.Platform.iOS/Renderers/EntryRenderer.cs @@ -0,0 +1,184 @@ +using System; +using System.Drawing; +using System.Runtime.Remoting.Channels; +using System.ComponentModel; +#if __UNIFIED__ +using UIKit; + +#else +using MonoTouch.UIKit; +#endif + +namespace Xamarin.Forms.Platform.iOS +{ + public class EntryRenderer : ViewRenderer<Entry, UITextField> + { + UIColor _defaultTextColor; + + public EntryRenderer() + { + Frame = new RectangleF(0, 20, 320, 40); + } + + protected override void Dispose(bool disposing) + { + if (disposing) + { + if (Control != null) + { + Control.EditingDidBegin -= OnEditingBegan; + Control.EditingChanged -= OnEditingChanged; + Control.EditingDidEnd -= OnEditingEnded; + } + } + + base.Dispose(disposing); + } + + protected override void OnElementChanged(ElementChangedEventArgs<Entry> e) + { + base.OnElementChanged(e); + + var textField = Control; + + if (Control == null) + { + SetNativeControl(textField = new UITextField(RectangleF.Empty)); + + _defaultTextColor = textField.TextColor; + textField.BorderStyle = UITextBorderStyle.RoundedRect; + + textField.EditingChanged += OnEditingChanged; + + textField.ShouldReturn = OnShouldReturn; + + textField.EditingDidBegin += OnEditingBegan; + textField.EditingDidEnd += OnEditingEnded; + } + + if (e.NewElement != null) + { + UpdatePlaceholder(); + UpdatePassword(); + UpdateText(); + UpdateColor(); + UpdateFont(); + UpdateKeyboard(); + UpdateAlignment(); + } + } + + protected override void OnElementPropertyChanged(object sender, PropertyChangedEventArgs e) + { + if (e.PropertyName == Entry.PlaceholderProperty.PropertyName || e.PropertyName == Entry.PlaceholderColorProperty.PropertyName) + UpdatePlaceholder(); + else if (e.PropertyName == Entry.IsPasswordProperty.PropertyName) + UpdatePassword(); + else if (e.PropertyName == Entry.TextProperty.PropertyName) + UpdateText(); + else if (e.PropertyName == Entry.TextColorProperty.PropertyName) + UpdateColor(); + else if (e.PropertyName == Xamarin.Forms.InputView.KeyboardProperty.PropertyName) + UpdateKeyboard(); + else if (e.PropertyName == Entry.HorizontalTextAlignmentProperty.PropertyName) + UpdateAlignment(); + else if (e.PropertyName == Entry.FontAttributesProperty.PropertyName) + UpdateFont(); + else if (e.PropertyName == Entry.FontFamilyProperty.PropertyName) + UpdateFont(); + else if (e.PropertyName == Entry.FontSizeProperty.PropertyName) + UpdateFont(); + else if (e.PropertyName == VisualElement.IsEnabledProperty.PropertyName) + { + UpdateColor(); + UpdatePlaceholder(); + } + + base.OnElementPropertyChanged(sender, e); + } + + void OnEditingBegan(object sender, EventArgs e) + { + ((IElementController)Element).SetValueFromRenderer(VisualElement.IsFocusedPropertyKey, true); + } + + void OnEditingChanged(object sender, EventArgs eventArgs) + { + ((IElementController)Element).SetValueFromRenderer(Entry.TextProperty, Control.Text); + } + + void OnEditingEnded(object sender, EventArgs e) + { + ((IElementController)Element).SetValueFromRenderer(VisualElement.IsFocusedPropertyKey, false); + } + + bool OnShouldReturn(UITextField view) + { + Control.ResignFirstResponder(); + Element.SendCompleted(); + return true; + } + + void UpdateAlignment() + { + Control.TextAlignment = Element.HorizontalTextAlignment.ToNativeTextAlignment(); + } + + void UpdateColor() + { + var textColor = Element.TextColor; + + if (textColor.IsDefault || !Element.IsEnabled) + Control.TextColor = _defaultTextColor; + else + Control.TextColor = textColor.ToUIColor(); + } + + void UpdateFont() + { + Control.Font = Element.ToUIFont(); + } + + void UpdateKeyboard() + { + Control.ApplyKeyboard(Element.Keyboard); + } + + void UpdatePassword() + { + if (Element.IsPassword && Control.IsFirstResponder) + { + Control.Enabled = false; + Control.SecureTextEntry = true; + Control.Enabled = Element.IsEnabled; + Control.BecomeFirstResponder(); + } + else + Control.SecureTextEntry = Element.IsPassword; + } + + void UpdatePlaceholder() + { + var formatted = (FormattedString)Element.Placeholder; + + if (formatted == null) + return; + + var targetColor = Element.PlaceholderColor; + + // Placeholder default color is 70% gray + // https://developer.apple.com/library/prerelease/ios/documentation/UIKit/Reference/UITextField_Class/index.html#//apple_ref/occ/instp/UITextField/placeholder + + var color = Element.IsEnabled && !targetColor.IsDefault ? targetColor : ColorExtensions.SeventyPercentGrey.ToColor(); + + Control.AttributedPlaceholder = formatted.ToAttributed(Element, color); + } + + void UpdateText() + { + // ReSharper disable once RedundantCheckBeforeAssignment + if (Control.Text != Element.Text) + Control.Text = Element.Text; + } + } +}
\ No newline at end of file diff --git a/Xamarin.Forms.Platform.iOS/Renderers/ExportCellAttribute.cs b/Xamarin.Forms.Platform.iOS/Renderers/ExportCellAttribute.cs new file mode 100644 index 00000000..1abf0d7f --- /dev/null +++ b/Xamarin.Forms.Platform.iOS/Renderers/ExportCellAttribute.cs @@ -0,0 +1,11 @@ +using System; + +namespace Xamarin.Forms +{ + [AttributeUsage (AttributeTargets.Assembly, AllowMultiple = true)] + public sealed class ExportCellAttribute : HandlerAttribute { + public ExportCellAttribute (Type handler, Type target) + : base (handler, target) { + } + } +}
\ No newline at end of file diff --git a/Xamarin.Forms.Platform.iOS/Renderers/ExportImageSourceHandlerAttribute.cs b/Xamarin.Forms.Platform.iOS/Renderers/ExportImageSourceHandlerAttribute.cs new file mode 100644 index 00000000..59e96e1f --- /dev/null +++ b/Xamarin.Forms.Platform.iOS/Renderers/ExportImageSourceHandlerAttribute.cs @@ -0,0 +1,11 @@ +using System; + +namespace Xamarin.Forms +{ + [AttributeUsage (AttributeTargets.Assembly, AllowMultiple = true)] + public sealed class ExportImageSourceHandlerAttribute : HandlerAttribute { + public ExportImageSourceHandlerAttribute (Type handler, Type target) + : base (handler, target) { + } + } +}
\ No newline at end of file diff --git a/Xamarin.Forms.Platform.iOS/Renderers/ExportRendererAttribute.cs b/Xamarin.Forms.Platform.iOS/Renderers/ExportRendererAttribute.cs new file mode 100644 index 00000000..899056da --- /dev/null +++ b/Xamarin.Forms.Platform.iOS/Renderers/ExportRendererAttribute.cs @@ -0,0 +1,30 @@ +using System; +#if __UNIFIED__ +using UIKit; +#else +using MonoTouch.UIKit; +#endif + +namespace Xamarin.Forms +{ + [AttributeUsage (AttributeTargets.Assembly, AllowMultiple = true)] + public sealed class ExportRendererAttribute : HandlerAttribute { + internal bool Idiomatic { get; private set; } + internal UIUserInterfaceIdiom Idiom { get; private set; } + + public ExportRendererAttribute (Type handler, Type target, UIUserInterfaceIdiom idiom) + : base (handler, target) { + Idiomatic = true; + Idiom = idiom; + } + + public ExportRendererAttribute (Type handler, Type target) + : base (handler, target) { + Idiomatic = false; + } + + public override bool ShouldRegister () { + return !Idiomatic || Idiom == UIDevice.CurrentDevice.UserInterfaceIdiom; + } + } +}
\ No newline at end of file diff --git a/Xamarin.Forms.Platform.iOS/Renderers/FontExtensions.cs b/Xamarin.Forms.Platform.iOS/Renderers/FontExtensions.cs new file mode 100644 index 00000000..07afcfaf --- /dev/null +++ b/Xamarin.Forms.Platform.iOS/Renderers/FontExtensions.cs @@ -0,0 +1,205 @@ +using System; +using System.Collections.Generic; +using System.Diagnostics; +using System.Linq; +#if __UNIFIED__ +using UIKit; + +#else +using MonoTouch.UIKit; +#endif + +namespace Xamarin.Forms.Platform.iOS +{ + public static class FontExtensions + { + static readonly Dictionary<ToUIFontKey, UIFont> ToUiFont = new Dictionary<ToUIFontKey, UIFont>(); + + public static UIFont ToUIFont(this Font self) + { + var size = (float)self.FontSize; + if (self.UseNamedSize) + { + switch (self.NamedSize) + { + case NamedSize.Micro: + size = 12; + break; + case NamedSize.Small: + size = 14; + break; + case NamedSize.Medium: + size = 17; // as defined by iOS documentation + break; + case NamedSize.Large: + size = 22; + break; + default: + size = 17; + break; + } + } + + var bold = self.FontAttributes.HasFlag(FontAttributes.Bold); + var italic = self.FontAttributes.HasFlag(FontAttributes.Italic); + + if (self.FontFamily != null) + { + try + { + if (UIFont.FamilyNames.Contains(self.FontFamily) && Forms.IsiOS7OrNewer) + { + var descriptor = new UIFontDescriptor().CreateWithFamily(self.FontFamily); + + if (bold || italic) + { + var traits = (UIFontDescriptorSymbolicTraits)0; + if (bold) + traits = traits | UIFontDescriptorSymbolicTraits.Bold; + if (italic) + traits = traits | UIFontDescriptorSymbolicTraits.Italic; + + descriptor = descriptor.CreateWithTraits(traits); + return UIFont.FromDescriptor(descriptor, size); + } + } + + return UIFont.FromName(self.FontFamily, size); + } + catch + { + Debug.WriteLine("Could not load font named: {0}", self.FontFamily); + } + } + if (bold && italic) + { + if (!Forms.IsiOS7OrNewer) + { + // not sure how to make a font both bold and italic in iOS 6, default to bold + return UIFont.BoldSystemFontOfSize(size); + } + + var defaultFont = UIFont.SystemFontOfSize(size); + var descriptor = defaultFont.FontDescriptor.CreateWithTraits(UIFontDescriptorSymbolicTraits.Bold | UIFontDescriptorSymbolicTraits.Italic); + return UIFont.FromDescriptor(descriptor, 0); + } + if (bold) + return UIFont.BoldSystemFontOfSize(size); + if (italic) + return UIFont.ItalicSystemFontOfSize(size); + return UIFont.SystemFontOfSize(size); + } + + internal static bool IsDefault(this Span self) + { + return self.FontFamily == null && self.FontSize == Device.GetNamedSize(NamedSize.Default, typeof(Label), true) && self.FontAttributes == FontAttributes.None; + } + + internal static UIFont ToUIFont(this Label label) + { + var values = label.GetValues(Label.FontFamilyProperty, Label.FontSizeProperty, Label.FontAttributesProperty); + return ToUIFont((string)values[0], (float)(double)values[1], (FontAttributes)values[2]) ?? UIFont.SystemFontOfSize(UIFont.LabelFontSize); + } + + internal static UIFont ToUIFont(this IFontElement element) + { + return ToUIFont(element.FontFamily, (float)element.FontSize, element.FontAttributes); + } + + static UIFont _ToUIFont(string family, float size, FontAttributes attributes) + { + var bold = (attributes & FontAttributes.Bold) != 0; + var italic = (attributes & FontAttributes.Italic) != 0; + + if (family != null) + { + try + { + UIFont result; + if (UIFont.FamilyNames.Contains(family) && Forms.IsiOS7OrNewer) + { + var descriptor = new UIFontDescriptor().CreateWithFamily(family); + + if (bold || italic) + { + var traits = (UIFontDescriptorSymbolicTraits)0; + if (bold) + traits = traits | UIFontDescriptorSymbolicTraits.Bold; + if (italic) + traits = traits | UIFontDescriptorSymbolicTraits.Italic; + + descriptor = descriptor.CreateWithTraits(traits); + result = UIFont.FromDescriptor(descriptor, size); + if (result != null) + return result; + } + } + + result = UIFont.FromName(family, size); + if (result != null) + return result; + } + catch + { + Debug.WriteLine("Could not load font named: {0}", family); + } + } + + if (bold && italic) + { + var defaultFont = UIFont.SystemFontOfSize(size); + + if (!Forms.IsiOS7OrNewer) + { + // not sure how to make a font both bold and italic in iOS 6, default to bold + return UIFont.BoldSystemFontOfSize(size); + } + + var descriptor = defaultFont.FontDescriptor.CreateWithTraits(UIFontDescriptorSymbolicTraits.Bold | UIFontDescriptorSymbolicTraits.Italic); + return UIFont.FromDescriptor(descriptor, 0); + } + if (bold) + return UIFont.BoldSystemFontOfSize(size); + if (italic) + return UIFont.ItalicSystemFontOfSize(size); + + return UIFont.SystemFontOfSize(size); + } + + static UIFont ToUIFont(string family, float size, FontAttributes attributes) + { + var key = new ToUIFontKey(family, size, attributes); + + lock(ToUiFont) + { + UIFont value; + if (ToUiFont.TryGetValue(key, out value)) + return value; + } + + var generatedValue = _ToUIFont(family, size, attributes); + + lock(ToUiFont) + { + UIFont value; + if (!ToUiFont.TryGetValue(key, out value)) + ToUiFont.Add(key, value = generatedValue); + return value; + } + } + + struct ToUIFontKey + { + internal ToUIFontKey(string family, float size, FontAttributes attributes) + { + _family = family; + _size = size; + _attributes = attributes; + } + + string _family; + float _size; + FontAttributes _attributes; + } + } +}
\ No newline at end of file diff --git a/Xamarin.Forms.Platform.iOS/Renderers/FormattedStringExtensions.cs b/Xamarin.Forms.Platform.iOS/Renderers/FormattedStringExtensions.cs new file mode 100644 index 00000000..0dfa38cd --- /dev/null +++ b/Xamarin.Forms.Platform.iOS/Renderers/FormattedStringExtensions.cs @@ -0,0 +1,82 @@ +#if __UNIFIED__ +using Foundation; +using UIKit; + +#else +using MonoTouch.Foundation; +using MonoTouch.UIKit; +#endif + +namespace Xamarin.Forms.Platform.iOS +{ + public static class FormattedStringExtensions + { + public static NSAttributedString ToAttributed(this Span span, Font defaultFont, Color defaultForegroundColor) + { + if (span == null) + return null; + + var font = span.Font != Font.Default ? span.Font : defaultFont; + + var fgcolor = span.ForegroundColor; + if (fgcolor.IsDefault) + fgcolor = defaultForegroundColor; + if (fgcolor.IsDefault) + fgcolor = Color.Black; // as defined by apple docs + + return new NSAttributedString(span.Text, font == Font.Default ? null : font.ToUIFont(), fgcolor.ToUIColor(), span.BackgroundColor.ToUIColor()); + } + + public static NSAttributedString ToAttributed(this FormattedString formattedString, Font defaultFont, Color defaultForegroundColor) + { + if (formattedString == null) + return null; + var attributed = new NSMutableAttributedString(); + foreach (var span in formattedString.Spans) + { + if (span.Text == null) + continue; + + attributed.Append(span.ToAttributed(defaultFont, defaultForegroundColor)); + } + + return attributed; + } + + internal static NSAttributedString ToAttributed(this Span span, Element owner, Color defaultForegroundColor) + { + if (span == null) + return null; + + UIFont targetFont; + if (span.IsDefault()) + targetFont = ((IFontElement)owner).ToUIFont(); + else + targetFont = span.ToUIFont(); + + var fgcolor = span.ForegroundColor; + if (fgcolor.IsDefault) + fgcolor = defaultForegroundColor; + if (fgcolor.IsDefault) + fgcolor = Color.Black; // as defined by apple docs + + return new NSAttributedString(span.Text, targetFont, fgcolor.ToUIColor(), span.BackgroundColor.ToUIColor()); + } + + internal static NSAttributedString ToAttributed(this FormattedString formattedString, Element owner, Color defaultForegroundColor) + { + if (formattedString == null) + return null; + var attributed = new NSMutableAttributedString(); + foreach (var span in formattedString.Spans) + { + if (span.Text == null) + continue; + + attributed.Append(span.ToAttributed(owner, defaultForegroundColor)); + } + + return attributed; + } + } +}
\ No newline at end of file diff --git a/Xamarin.Forms.Platform.iOS/Renderers/FrameRenderer.cs b/Xamarin.Forms.Platform.iOS/Renderers/FrameRenderer.cs new file mode 100644 index 00000000..3f2f1ccb --- /dev/null +++ b/Xamarin.Forms.Platform.iOS/Renderers/FrameRenderer.cs @@ -0,0 +1,61 @@ +using System.ComponentModel; +using System.Drawing; +#if __UNIFIED__ +using UIKit; + +#else +using MonoTouch.UIKit; +#endif + +namespace Xamarin.Forms.Platform.iOS +{ + public class FrameRenderer : VisualElementRenderer<Frame> + { + protected override void OnElementChanged(ElementChangedEventArgs<Frame> e) + { + base.OnElementChanged(e); + + if (e.NewElement != null) + SetupLayer(); + } + + protected override void OnElementPropertyChanged(object sender, PropertyChangedEventArgs e) + { + base.OnElementPropertyChanged(sender, e); + + if (e.PropertyName == VisualElement.BackgroundColorProperty.PropertyName || e.PropertyName == Xamarin.Forms.Frame.OutlineColorProperty.PropertyName || + e.PropertyName == Xamarin.Forms.Frame.HasShadowProperty.PropertyName) + SetupLayer(); + } + + void SetupLayer() + { + Layer.CornerRadius = 5; + if (Element.BackgroundColor == Color.Default) + Layer.BackgroundColor = UIColor.White.CGColor; + else + Layer.BackgroundColor = Element.BackgroundColor.ToCGColor(); + + if (Element.HasShadow) + { + Layer.ShadowRadius = 5; + Layer.ShadowColor = UIColor.Black.CGColor; + Layer.ShadowOpacity = 0.8f; + Layer.ShadowOffset = new SizeF(); + } + else + Layer.ShadowOpacity = 0; + + if (Element.OutlineColor == Color.Default) + Layer.BorderColor = UIColor.Clear.CGColor; + else + { + Layer.BorderColor = Element.OutlineColor.ToCGColor(); + Layer.BorderWidth = 1; + } + + Layer.RasterizationScale = UIScreen.MainScreen.Scale; + Layer.ShouldRasterize = true; + } + } +}
\ No newline at end of file diff --git a/Xamarin.Forms.Platform.iOS/Renderers/ImageRenderer.cs b/Xamarin.Forms.Platform.iOS/Renderers/ImageRenderer.cs new file mode 100644 index 00000000..f227f9a5 --- /dev/null +++ b/Xamarin.Forms.Platform.iOS/Renderers/ImageRenderer.cs @@ -0,0 +1,209 @@ +using System.Drawing; +using System.ComponentModel; +using System.IO; +using System.Threading.Tasks; +using System.Threading; +using System; +#if __UNIFIED__ +using UIKit; +using Foundation; +#else +using MonoTouch.UIKit; +using MonoTouch.Foundation; +#endif +#if __UNIFIED__ +using RectangleF = CoreGraphics.CGRect; +using SizeF = CoreGraphics.CGSize; +using PointF = CoreGraphics.CGPoint; + +#else +using nfloat=System.Single; +using nint=System.Int32; +using nuint=System.UInt32; +#endif + +namespace Xamarin.Forms.Platform.iOS +{ + public static class ImageExtensions + { + public static UIViewContentMode ToUIViewContentMode(this Aspect aspect) + { + switch (aspect) + { + case Aspect.AspectFill: + return UIViewContentMode.ScaleAspectFill; + case Aspect.Fill: + return UIViewContentMode.ScaleToFill; + case Aspect.AspectFit: + default: + return UIViewContentMode.ScaleAspectFit; + } + } + } + + public class ImageRenderer : ViewRenderer<Image, UIImageView> + { + bool _isDisposed; + + protected override void Dispose(bool disposing) + { + if (_isDisposed) + return; + + if (disposing) + { + UIImage oldUIImage; + if (Control != null && (oldUIImage = Control.Image) != null) + { + oldUIImage.Dispose(); + oldUIImage = null; + } + } + + _isDisposed = true; + + base.Dispose(disposing); + } + + protected override void OnElementChanged(ElementChangedEventArgs<Image> e) + { + if (Control == null) + { + var imageView = new UIImageView(RectangleF.Empty); + imageView.ContentMode = UIViewContentMode.ScaleAspectFit; + imageView.ClipsToBounds = true; + SetNativeControl(imageView); + } + + if (e.NewElement != null) + { + SetAspect(); + SetImage(e.OldElement); + SetOpacity(); + } + + base.OnElementChanged(e); + } + + protected override void OnElementPropertyChanged(object sender, PropertyChangedEventArgs e) + { + base.OnElementPropertyChanged(sender, e); + if (e.PropertyName == Image.SourceProperty.PropertyName) + SetImage(); + else if (e.PropertyName == Image.IsOpaqueProperty.PropertyName) + SetOpacity(); + else if (e.PropertyName == Image.AspectProperty.PropertyName) + SetAspect(); + } + + void SetAspect() + { + Control.ContentMode = Element.Aspect.ToUIViewContentMode(); + } + + async void SetImage(Image oldElement = null) + { + var source = Element.Source; + + if (oldElement != null) + { + var oldSource = oldElement.Source; + if (Equals(oldSource, source)) + return; + + if (oldSource is FileImageSource && source is FileImageSource && ((FileImageSource)oldSource).File == ((FileImageSource)source).File) + return; + + Control.Image = null; + } + + IImageSourceHandler handler; + ((IElementController)Element).SetValueFromRenderer(Image.IsLoadingPropertyKey, true); + + if (source != null && (handler = Registrar.Registered.GetHandler<IImageSourceHandler>(source.GetType())) != null) + { + UIImage uiimage; + try + { + uiimage = await handler.LoadImageAsync(source, scale: (float)UIScreen.MainScreen.Scale); + } + catch (OperationCanceledException) + { + uiimage = null; + } + + var imageView = Control; + if (imageView != null) + imageView.Image = uiimage; + + if (!_isDisposed) + ((IVisualElementController)Element).NativeSizeChanged(); + } + else + Control.Image = null; + + if (!_isDisposed) + ((IElementController)Element).SetValueFromRenderer(Image.IsLoadingPropertyKey, false); + } + + void SetOpacity() + { + Control.Opaque = Element.IsOpaque; + } + } + + public interface IImageSourceHandler : IRegisterable + { + Task<UIImage> LoadImageAsync(ImageSource imagesource, CancellationToken cancelationToken = default(CancellationToken), float scale = 1); + } + + public sealed class FileImageSourceHandler : IImageSourceHandler + { + public Task<UIImage> LoadImageAsync(ImageSource imagesource, CancellationToken cancelationToken = default(CancellationToken), float scale = 1f) + { + UIImage image = null; + var filesource = imagesource as FileImageSource; + if (filesource != null) + { + var file = filesource.File; + if (!string.IsNullOrEmpty(file)) + image = File.Exists(file) ? new UIImage(file) : UIImage.FromBundle(file); + } + return Task.FromResult(image); + } + } + + public sealed class StreamImagesourceHandler : IImageSourceHandler + { + public async Task<UIImage> LoadImageAsync(ImageSource imagesource, CancellationToken cancelationToken = default(CancellationToken), float scale = 1f) + { + UIImage image = null; + var streamsource = imagesource as StreamImageSource; + if (streamsource != null && streamsource.Stream != null) + { + var streamImage = await streamsource.GetStreamAsync(cancelationToken).ConfigureAwait(false); + if (streamImage != null) + image = UIImage.LoadFromData(NSData.FromStream(streamImage), scale); + } + return image; + } + } + + public sealed class ImageLoaderSourceHandler : IImageSourceHandler + { + public async Task<UIImage> LoadImageAsync(ImageSource imagesource, CancellationToken cancelationToken = default(CancellationToken), float scale = 1f) + { + UIImage image = null; + var imageLoader = imagesource as UriImageSource; + if (imageLoader != null && imageLoader.Uri != null) + { + using(var streamImage = await imageLoader.GetStreamAsync(cancelationToken).ConfigureAwait(false)) + { + if (streamImage != null) + image = UIImage.LoadFromData(NSData.FromStream(streamImage), scale); + } + } + return image; + } + } +}
\ No newline at end of file diff --git a/Xamarin.Forms.Platform.iOS/Renderers/KeyboardInsetTracker.cs b/Xamarin.Forms.Platform.iOS/Renderers/KeyboardInsetTracker.cs new file mode 100644 index 00000000..d5759947 --- /dev/null +++ b/Xamarin.Forms.Platform.iOS/Renderers/KeyboardInsetTracker.cs @@ -0,0 +1,109 @@ +using System; +using System.Drawing; +#if __UNIFIED__ +using UIKit; +#else +using MonoTouch.UIKit; +#endif +#if __UNIFIED__ +using RectangleF = CoreGraphics.CGRect; +using SizeF = CoreGraphics.CGSize; +using PointF = CoreGraphics.CGPoint; + +#else +using nfloat=System.Single; +using nint=System.Int32; +using nuint=System.UInt32; +#endif + +namespace Xamarin.Forms.Platform.iOS +{ + internal class KeyboardInsetTracker : IDisposable + { + readonly Func<UIWindow> _fetchWindow; + readonly Action<PointF> _setContentOffset; + readonly Action<UIEdgeInsets> _setInsetAction; + readonly UIView _targetView; + bool _disposed; + + RectangleF _lastKeyboardRect; + + public KeyboardInsetTracker(UIView targetView, Func<UIWindow> fetchWindow, Action<UIEdgeInsets> setInsetAction) : this(targetView, fetchWindow, setInsetAction, null) + { + } + + public KeyboardInsetTracker(UIView targetView, Func<UIWindow> fetchWindow, Action<UIEdgeInsets> setInsetAction, Action<PointF> setContentOffset) + { + _setContentOffset = setContentOffset; + _targetView = targetView; + _fetchWindow = fetchWindow; + _setInsetAction = setInsetAction; + KeyboardObserver.KeyboardWillShow += OnKeyboardShown; + KeyboardObserver.KeyboardWillHide += OnKeyboardHidden; + } + + public void Dispose() + { + if (_disposed) + return; + _disposed = true; + + KeyboardObserver.KeyboardWillShow -= OnKeyboardShown; + KeyboardObserver.KeyboardWillHide -= OnKeyboardHidden; + } + + //This method allows us to update the insets if the Frame changes + internal void UpdateInsets() + { + //being called from LayoutSubviews but keyboard wasn't shown yet + if (_lastKeyboardRect.IsEmpty) + return; + + var window = _fetchWindow(); + // Code left verbose to make its operation more obvious + if (window == null) + { + // we are not currently displayed and can safely ignore this + // most likely this renderer is on a page which is currently not displayed (e.g. in NavController) + return; + } + + var field = _targetView.FindFirstResponder(); + + //the view that is triggering the keyboard is not inside our UITableView? + //if (field == null) + // return; + + var boundsSize = _targetView.Frame.Size; + + //since our keyboard frame is RVC CoordinateSpace, lets convert it to our targetView CoordinateSpace + var rect = _targetView.Superview.ConvertRectFromView(_lastKeyboardRect, null); + //let's see how much does it cover our target view + var overlay = RectangleF.Intersect(rect, _targetView.Frame); + + _setInsetAction(new UIEdgeInsets(0, 0, overlay.Height, 0)); + + if (field is UITextView && _setContentOffset != null) + { + var keyboardTop = boundsSize.Height - overlay.Height; + var fieldPosition = field.ConvertPointToView(field.Frame.Location, _targetView.Superview); + var fieldBottom = fieldPosition.Y + field.Frame.Height; + var offset = fieldBottom - keyboardTop; + if (offset > 0) + _setContentOffset(new PointF(0, offset)); + } + } + + void OnKeyboardHidden(object sender, UIKeyboardEventArgs args) + { + _setInsetAction(new UIEdgeInsets(0, 0, 0, 0)); + _lastKeyboardRect = RectangleF.Empty; + } + + void OnKeyboardShown(object sender, UIKeyboardEventArgs args) + { + _lastKeyboardRect = args.FrameEnd; + UpdateInsets(); + } + } +}
\ No newline at end of file diff --git a/Xamarin.Forms.Platform.iOS/Renderers/KeyboardObserver.cs b/Xamarin.Forms.Platform.iOS/Renderers/KeyboardObserver.cs new file mode 100644 index 00000000..4ebb75d7 --- /dev/null +++ b/Xamarin.Forms.Platform.iOS/Renderers/KeyboardObserver.cs @@ -0,0 +1,37 @@ +using System; +#if __UNIFIED__ +using UIKit; + +#else +using MonoTouch.UIKit; +#endif + +namespace Xamarin.Forms.Platform.iOS +{ + internal static class KeyboardObserver + { + static KeyboardObserver() + { + UIKeyboard.Notifications.ObserveWillShow(OnKeyboardShown); + UIKeyboard.Notifications.ObserveWillHide(OnKeyboardHidden); + } + + public static event EventHandler<UIKeyboardEventArgs> KeyboardWillHide; + + public static event EventHandler<UIKeyboardEventArgs> KeyboardWillShow; + + static void OnKeyboardHidden(object sender, UIKeyboardEventArgs args) + { + var handler = KeyboardWillHide; + if (handler != null) + handler(sender, args); + } + + static void OnKeyboardShown(object sender, UIKeyboardEventArgs args) + { + var handler = KeyboardWillShow; + if (handler != null) + handler(sender, args); + } + } +}
\ No newline at end of file diff --git a/Xamarin.Forms.Platform.iOS/Renderers/LabelRenderer.cs b/Xamarin.Forms.Platform.iOS/Renderers/LabelRenderer.cs new file mode 100644 index 00000000..f886cb35 --- /dev/null +++ b/Xamarin.Forms.Platform.iOS/Renderers/LabelRenderer.cs @@ -0,0 +1,183 @@ +using System; +using System.Drawing; +using System.ComponentModel; +#if __UNIFIED__ +using UIKit; +using CoreText; +#else +using MonoTouch.UIKit; +using MonoTouch.CoreText; +#endif +#if __UNIFIED__ +using RectangleF = CoreGraphics.CGRect; +using SizeF = CoreGraphics.CGSize; +using PointF = CoreGraphics.CGPoint; + +#else +using nfloat=System.Single; +using nint=System.Int32; +using nuint=System.UInt32; +#endif + +namespace Xamarin.Forms.Platform.iOS +{ + public class LabelRenderer : ViewRenderer<Label, UILabel> + { + SizeRequest _perfectSize; + + bool _perfectSizeValid; + + public override SizeRequest GetDesiredSize(double widthConstraint, double heightConstraint) + { + if (!_perfectSizeValid) + { + _perfectSize = base.GetDesiredSize(double.PositiveInfinity, double.PositiveInfinity); + _perfectSize.Minimum = new Size(Math.Min(10, _perfectSize.Request.Width), _perfectSize.Request.Height); + _perfectSizeValid = true; + } + + if (widthConstraint >= _perfectSize.Request.Width && heightConstraint >= _perfectSize.Request.Height) + return _perfectSize; + + var result = base.GetDesiredSize(widthConstraint, heightConstraint); + result.Minimum = new Size(Math.Min(10, result.Request.Width), result.Request.Height); + if ((Element.LineBreakMode & (LineBreakMode.TailTruncation | LineBreakMode.HeadTruncation | LineBreakMode.MiddleTruncation)) != 0) + { + if (result.Request.Width > widthConstraint) + result.Request = new Size(Math.Max(result.Minimum.Width, widthConstraint), result.Request.Height); + } + + return result; + } + + public override void LayoutSubviews() + { + base.LayoutSubviews(); + if (Control == null) + return; + + SizeF fitSize; + nfloat labelHeight; + switch (Element.VerticalTextAlignment) + { + case TextAlignment.Start: + fitSize = Control.SizeThatFits(Element.Bounds.Size.ToSizeF()); + labelHeight = (nfloat)Math.Min(Bounds.Height, fitSize.Height); + Control.Frame = new RectangleF(0, 0, (nfloat)Element.Width, labelHeight); + break; + case TextAlignment.Center: + Control.Frame = new RectangleF(0, 0, (nfloat)Element.Width, (nfloat)Element.Height); + break; + case TextAlignment.End: + nfloat yOffset = 0; + fitSize = Control.SizeThatFits(Element.Bounds.Size.ToSizeF()); + labelHeight = (nfloat)Math.Min(Bounds.Height, fitSize.Height); + yOffset = (nfloat)(Element.Height - labelHeight); + Control.Frame = new RectangleF(0, yOffset, (nfloat)Element.Width, labelHeight); + break; + } + } + + protected override void OnElementChanged(ElementChangedEventArgs<Label> e) + { + if (e.NewElement != null) + { + if (Control == null) + { + SetNativeControl(new UILabel(RectangleF.Empty) { BackgroundColor = UIColor.Clear }); + } + + UpdateText(); + UpdateLineBreakMode(); + UpdateAlignment(); + } + + base.OnElementChanged(e); + } + + protected override void OnElementPropertyChanged(object sender, PropertyChangedEventArgs e) + { + base.OnElementPropertyChanged(sender, e); + + if (e.PropertyName == Label.HorizontalTextAlignmentProperty.PropertyName) + UpdateAlignment(); + else if (e.PropertyName == Label.VerticalTextAlignmentProperty.PropertyName) + LayoutSubviews(); + else if (e.PropertyName == Label.TextColorProperty.PropertyName) + UpdateText(); + else if (e.PropertyName == Label.FontProperty.PropertyName) + UpdateText(); + else if (e.PropertyName == Label.TextProperty.PropertyName) + UpdateText(); + else if (e.PropertyName == Label.FormattedTextProperty.PropertyName) + UpdateText(); + else if (e.PropertyName == Label.LineBreakModeProperty.PropertyName) + UpdateLineBreakMode(); + } + + protected override void SetBackgroundColor(Color color) + { + if (color == Color.Default) + BackgroundColor = UIColor.Clear; + else + BackgroundColor = color.ToUIColor(); + } + + void UpdateAlignment() + { + Control.TextAlignment = Element.HorizontalTextAlignment.ToNativeTextAlignment(); + } + + void UpdateLineBreakMode() + { + _perfectSizeValid = false; + + switch (Element.LineBreakMode) + { + case LineBreakMode.NoWrap: + Control.LineBreakMode = UILineBreakMode.Clip; + Control.Lines = 1; + break; + case LineBreakMode.WordWrap: + Control.LineBreakMode = UILineBreakMode.WordWrap; + Control.Lines = 0; + break; + case LineBreakMode.CharacterWrap: + Control.LineBreakMode = UILineBreakMode.CharacterWrap; + Control.Lines = 0; + break; + case LineBreakMode.HeadTruncation: + Control.LineBreakMode = UILineBreakMode.HeadTruncation; + Control.Lines = 1; + break; + case LineBreakMode.MiddleTruncation: + Control.LineBreakMode = UILineBreakMode.MiddleTruncation; + Control.Lines = 1; + break; + case LineBreakMode.TailTruncation: + Control.LineBreakMode = UILineBreakMode.TailTruncation; + Control.Lines = 1; + break; + } + } + + void UpdateText() + { + _perfectSizeValid = false; + + var values = Element.GetValues(Label.FormattedTextProperty, Label.TextProperty, Label.TextColorProperty); + var formatted = (FormattedString)values[0]; + if (formatted != null) + Control.AttributedText = formatted.ToAttributed(Element, (Color)values[2]); + else + { + Control.Text = (string)values[1]; + // default value of color documented to be black in iOS docs + Control.Font = Element.ToUIFont(); + Control.TextColor = ((Color)values[2]).ToUIColor(ColorExtensions.Black); + } + + LayoutSubviews(); + } + } +}
\ No newline at end of file diff --git a/Xamarin.Forms.Platform.iOS/Renderers/ListViewRenderer.cs b/Xamarin.Forms.Platform.iOS/Renderers/ListViewRenderer.cs new file mode 100644 index 00000000..2151d446 --- /dev/null +++ b/Xamarin.Forms.Platform.iOS/Renderers/ListViewRenderer.cs @@ -0,0 +1,1119 @@ +using System; +using System.Collections; +using System.Collections.Generic; +using System.Collections.Specialized; +using System.ComponentModel; +using System.Drawing; +using System.Linq; +#if __UNIFIED__ +using UIKit; +using Foundation; +#else +using MonoTouch.UIKit; +using MonoTouch.Foundation; +#endif +#if __UNIFIED__ +using RectangleF = CoreGraphics.CGRect; +using SizeF = CoreGraphics.CGSize; +using PointF = CoreGraphics.CGPoint; + +#else +using nfloat=System.Single; +using nint=System.Int32; +using nuint=System.UInt32; +#endif + +namespace Xamarin.Forms.Platform.iOS +{ + public class ListViewRenderer : ViewRenderer<ListView, UITableView> + { + const int DefaultRowHeight = 44; + ListViewDataSource _dataSource; + bool _estimatedRowHeight; + IVisualElementRenderer _headerRenderer; + IVisualElementRenderer _footerRenderer; + + KeyboardInsetTracker _insetTracker; + RectangleF _previousFrame; + ScrollToRequestedEventArgs _requestedScroll; + bool _shouldEstimateRowHeight = true; + FormsUITableViewController _tableViewController; + + public override SizeRequest GetDesiredSize(double widthConstraint, double heightConstraint) + { + return Control.GetSizeRequest(widthConstraint, heightConstraint, 44, 44); + } + + public override void LayoutSubviews() + { + base.LayoutSubviews(); + + double height = Bounds.Height; + double width = Bounds.Width; + if (_headerRenderer != null) + { + var e = _headerRenderer.Element; + var request = e.Measure(width, double.PositiveInfinity, MeasureFlags.IncludeMargins); + + // Time for another story with Jason. Gather round children because the following Math.Ceiling will look like it's completely useless. + // You will remove it and test and find everything is fiiiiiine, but it is not fine, no it is far from fine. See iOS, or at least iOS 8 + // has an issue where-by if the TableHeaderView happens to NOT be an integer height, it will add padding to the space between the content + // of the UITableView and the TableHeaderView to the tune of the difference between Math.Ceiling (height) - height. Now this seems fine + // and when you test it will be, EXCEPT that it does this every time you toggle the visibility of the UITableView causing the spacing to + // grow a little each time, which you weren't testing at all were you? So there you have it, the stupid reason we integer align here. + // + // The same technically applies to the footer, though that could hardly matter less. We just do it for fun. + Layout.LayoutChildIntoBoundingRegion(e, new Rectangle(0, 0, width, Math.Ceiling(request.Request.Height))); + + Device.BeginInvokeOnMainThread(() => + { + if (_headerRenderer != null) + Control.TableHeaderView = _headerRenderer.NativeView; + }); + } + + if (_footerRenderer != null) + { + var e = _footerRenderer.Element; + var request = e.Measure(width, height, MeasureFlags.IncludeMargins); + Layout.LayoutChildIntoBoundingRegion(e, new Rectangle(0, 0, width, Math.Ceiling(request.Request.Height))); + + Device.BeginInvokeOnMainThread(() => + { + if (_footerRenderer != null) + Control.TableFooterView = _footerRenderer.NativeView; + }); + } + + if (_requestedScroll != null && Superview != null) + { + var request = _requestedScroll; + _requestedScroll = null; + OnScrollToRequested(this, request); + } + + if (_previousFrame != Frame) + { + _previousFrame = Frame; + _insetTracker?.UpdateInsets(); + } + } + + protected override void Dispose(bool disposing) + { + // check inset tracker for null to + if (disposing && _insetTracker != null) + { + _insetTracker.Dispose(); + _insetTracker = null; + + var viewsToLookAt = new Stack<UIView>(Subviews); + while (viewsToLookAt.Count > 0) + { + var view = viewsToLookAt.Pop(); + var viewCellRenderer = view as ViewCellRenderer.ViewTableCell; + if (viewCellRenderer != null) + viewCellRenderer.Dispose(); + else + { + foreach (var child in view.Subviews) + viewsToLookAt.Push(child); + } + } + + if (Element != null) + { + Element.TemplatedItems.CollectionChanged -= OnCollectionChanged; + Element.TemplatedItems.GroupedCollectionChanged -= OnGroupedCollectionChanged; + } + + if (_tableViewController != null) + { + _tableViewController.Dispose(); + _tableViewController = null; + } + } + + if (disposing) + { + if (_headerRenderer != null) + { + var platform = _headerRenderer.Element.Platform as Platform; + if (platform != null) + platform.DisposeModelAndChildrenRenderers(_headerRenderer.Element); + _headerRenderer = null; + } + if (_footerRenderer != null) + { + var platform = _footerRenderer.Element.Platform as Platform; + if (platform != null) + platform.DisposeModelAndChildrenRenderers(_footerRenderer.Element); + _footerRenderer = null; + } + + var controller = Element as IListViewController; + + var headerView = controller?.HeaderElement as VisualElement; + if (headerView != null) + headerView.MeasureInvalidated -= OnHeaderMeasureInvalidated; + Control?.TableHeaderView?.Dispose(); + + var footerView = controller?.FooterElement as VisualElement; + if (footerView != null) + footerView.MeasureInvalidated -= OnFooterMeasureInvalidated; + Control?.TableFooterView?.Dispose(); + } + + base.Dispose(disposing); + } + + protected override void OnElementChanged(ElementChangedEventArgs<ListView> e) + { + _requestedScroll = null; + + if (e.OldElement != null) + { + var controller = (IListViewController)e.OldElement; + var headerView = (VisualElement)controller.HeaderElement; + if (headerView != null) + headerView.MeasureInvalidated -= OnHeaderMeasureInvalidated; + + var footerView = (VisualElement)controller.FooterElement; + if (footerView != null) + footerView.MeasureInvalidated -= OnFooterMeasureInvalidated; + + e.OldElement.ScrollToRequested -= OnScrollToRequested; + e.OldElement.TemplatedItems.CollectionChanged -= OnCollectionChanged; + e.OldElement.TemplatedItems.GroupedCollectionChanged -= OnGroupedCollectionChanged; + } + + if (e.NewElement != null) + { + if (Control == null) + { + _tableViewController = new FormsUITableViewController(e.NewElement); + SetNativeControl(_tableViewController.TableView); + if (Forms.IsiOS9OrNewer) + Control.CellLayoutMarginsFollowReadableWidth = false; + + _insetTracker = new KeyboardInsetTracker(_tableViewController.TableView, () => Control.Window, insets => Control.ContentInset = Control.ScrollIndicatorInsets = insets, point => + { + var offset = Control.ContentOffset; + offset.Y += point.Y; + Control.SetContentOffset(offset, true); + }); + } + _shouldEstimateRowHeight = true; + //if the user specifies he wants to sacrifice performance we will do things like: + // - don't EstimateRowHeight anymore + if (e.NewElement.TakePerformanceHit) + _shouldEstimateRowHeight = false; + + e.NewElement.ScrollToRequested += OnScrollToRequested; + e.NewElement.TemplatedItems.CollectionChanged += OnCollectionChanged; + e.NewElement.TemplatedItems.GroupedCollectionChanged += OnGroupedCollectionChanged; + + UpdateRowHeight(); + + Control.Source = _dataSource = e.NewElement.HasUnevenRows ? new UnevenListViewDataSource(e.NewElement, _tableViewController) : new ListViewDataSource(e.NewElement, _tableViewController); + + UpdateEstimatedRowHeight(); + UpdateHeader(); + UpdateFooter(); + UpdatePullToRefreshEnabled(); + UpdateIsRefreshing(); + UpdateSeparatorColor(); + UpdateSeparatorVisibility(); + + var selected = e.NewElement.SelectedItem; + if (selected != null) + _dataSource.OnItemSelected(null, new SelectedItemChangedEventArgs(selected)); + } + + base.OnElementChanged(e); + } + + protected override void OnElementPropertyChanged(object sender, PropertyChangedEventArgs e) + { + base.OnElementPropertyChanged(sender, e); + if (e.PropertyName == ListView.RowHeightProperty.PropertyName) + UpdateRowHeight(); + else if (e.PropertyName == ListView.IsGroupingEnabledProperty.PropertyName) + _dataSource.UpdateGrouping(); + else if (e.PropertyName == ListView.HasUnevenRowsProperty.PropertyName) + { + _estimatedRowHeight = false; + Control.Source = _dataSource = Element.HasUnevenRows ? new UnevenListViewDataSource(_dataSource) : new ListViewDataSource(_dataSource); + } + else if (e.PropertyName == ListView.IsPullToRefreshEnabledProperty.PropertyName) + UpdatePullToRefreshEnabled(); + else if (e.PropertyName == ListView.IsRefreshingProperty.PropertyName) + UpdateIsRefreshing(); + else if (e.PropertyName == ListView.SeparatorColorProperty.PropertyName) + UpdateSeparatorColor(); + else if (e.PropertyName == ListView.SeparatorVisibilityProperty.PropertyName) + UpdateSeparatorVisibility(); + else if (e.PropertyName == "HeaderElement") + UpdateHeader(); + else if (e.PropertyName == "FooterElement") + UpdateFooter(); + else if (e.PropertyName == "RefreshAllowed") + UpdatePullToRefreshEnabled(); + } + + NSIndexPath[] GetPaths(int section, int index, int count) + { + var paths = new NSIndexPath[count]; + for (var i = 0; i < paths.Length; i++) + paths[i] = NSIndexPath.FromRowSection(index + i, section); + + return paths; + } + + UITableViewScrollPosition GetScrollPosition(ScrollToPosition position) + { + switch (position) + { + case ScrollToPosition.Center: + return UITableViewScrollPosition.Middle; + case ScrollToPosition.End: + return UITableViewScrollPosition.Bottom; + case ScrollToPosition.Start: + return UITableViewScrollPosition.Top; + case ScrollToPosition.MakeVisible: + default: + return UITableViewScrollPosition.None; + } + } + + void OnCollectionChanged(object sender, NotifyCollectionChangedEventArgs e) + { + UpdateItems(e, 0, true); + } + + void OnFooterMeasureInvalidated(object sender, EventArgs eventArgs) + { + double width = Bounds.Width; + if (width == 0) + return; + + var footerView = (VisualElement)sender; + var request = footerView.Measure(width, double.PositiveInfinity, MeasureFlags.IncludeMargins); + Layout.LayoutChildIntoBoundingRegion(footerView, new Rectangle(0, 0, width, request.Request.Height)); + + Control.TableFooterView = _footerRenderer.NativeView; + } + + void OnGroupedCollectionChanged(object sender, NotifyCollectionChangedEventArgs e) + { + var til = (TemplatedItemsList<ItemsView<Cell>, Cell>)sender; + + var groupIndex = Element.TemplatedItems.IndexOf(til.HeaderContent); + UpdateItems(e, groupIndex, false); + } + + void OnHeaderMeasureInvalidated(object sender, EventArgs eventArgs) + { + double width = Bounds.Width; + if (width == 0) + return; + + var headerView = (VisualElement)sender; + var request = headerView.Measure(width, double.PositiveInfinity, MeasureFlags.IncludeMargins); + Layout.LayoutChildIntoBoundingRegion(headerView, new Rectangle(0, 0, width, request.Request.Height)); + + Control.TableHeaderView = _headerRenderer.NativeView; + } + + void OnScrollToRequested(object sender, ScrollToRequestedEventArgs e) + { + if (Superview == null) + { + _requestedScroll = e; + return; + } + + var position = GetScrollPosition(e.Position); + + if (Element.IsGroupingEnabled) + { + var result = Element.TemplatedItems.GetGroupAndIndexOfItem(e.Group, e.Item); + if (result.Item1 != -1 && result.Item2 != -1) + Control.ScrollToRow(NSIndexPath.FromRowSection(result.Item2, result.Item1), position, e.ShouldAnimate); + } + else + { + var index = Element.TemplatedItems.GetGlobalIndexOfItem(e.Item); + if (index != -1) + Control.ScrollToRow(NSIndexPath.FromRowSection(index, 0), position, e.ShouldAnimate); + } + } + + void UpdateEstimatedRowHeight() + { + if (_estimatedRowHeight) + return; + + var rowHeight = Element.RowHeight; + if (Element.HasUnevenRows && rowHeight == -1) + { + var source = _dataSource as UnevenListViewDataSource; + if (_shouldEstimateRowHeight) + { + if (Element.TemplatedItems.Count > 0 && source != null) + { + var estimatedHeightFromFirstCell = source.CalculateHeightForCell(Control, Element.TemplatedItems.First()); + Control.EstimatedRowHeight = estimatedHeightFromFirstCell; + _estimatedRowHeight = true; + } + else + { + //We need to set a default estimated row height, because re-setting it later(when we have items on the TIL) + //will cause the UITableView to reload, and throw a Excepetion + Control.EstimatedRowHeight = DefaultRowHeight; + } + } + } + else + { + if (Forms.IsiOS7OrNewer) + Control.EstimatedRowHeight = 0; + _estimatedRowHeight = true; + } + } + + void UpdateFooter() + { + var footer = ((IListViewController)Element).FooterElement; + var footerView = (View)footer; + + if (footerView != null) + { + if (_footerRenderer != null) + { + _footerRenderer.Element.MeasureInvalidated -= OnFooterMeasureInvalidated; + if (footer != null && _footerRenderer.GetType() == Registrar.Registered.GetHandlerType(footer.GetType())) + { + _footerRenderer.SetElement(footerView); + return; + } + Control.TableFooterView = null; + var platform = _footerRenderer.Element.Platform as Platform; + if (platform != null) + platform.DisposeModelAndChildrenRenderers(_footerRenderer.Element); + _footerRenderer.Dispose(); + _footerRenderer = null; + } + + _footerRenderer = Platform.CreateRenderer(footerView); + Platform.SetRenderer(footerView, _footerRenderer); + + double width = Bounds.Width; + var request = footerView.Measure(width, double.PositiveInfinity, MeasureFlags.IncludeMargins); + Layout.LayoutChildIntoBoundingRegion(footerView, new Rectangle(0, 0, width, request.Request.Height)); + + Control.TableFooterView = _footerRenderer.NativeView; + footerView.MeasureInvalidated += OnFooterMeasureInvalidated; + } + else if (_footerRenderer != null) + { + Control.TableFooterView = null; + var platform = _footerRenderer.Element.Platform as Platform; + if (platform != null) + platform.DisposeModelAndChildrenRenderers(_footerRenderer.Element); + _footerRenderer.Dispose(); + _footerRenderer = null; + } + } + + void UpdateHeader() + { + var header = ((IListViewController)Element).HeaderElement; + var headerView = (View)header; + + if (headerView != null) + { + if (_headerRenderer != null) + { + _headerRenderer.Element.MeasureInvalidated -= OnHeaderMeasureInvalidated; + if (header != null && _headerRenderer.GetType() == Registrar.Registered.GetHandlerType(header.GetType())) + { + _headerRenderer.SetElement(headerView); + return; + } + Control.TableHeaderView = null; + var platform = _headerRenderer.Element.Platform as Platform; + if (platform != null) + platform.DisposeModelAndChildrenRenderers(_headerRenderer.Element); + _headerRenderer = null; + } + + _headerRenderer = Platform.CreateRenderer(headerView); + // This will force measure to invalidate, which we haven't hooked up to yet because we are smarter! + Platform.SetRenderer(headerView, _headerRenderer); + + double width = Bounds.Width; + var request = headerView.Measure(width, double.PositiveInfinity, MeasureFlags.IncludeMargins); + Layout.LayoutChildIntoBoundingRegion(headerView, new Rectangle(0, 0, width, request.Request.Height)); + + Control.TableHeaderView = _headerRenderer.NativeView; + headerView.MeasureInvalidated += OnHeaderMeasureInvalidated; + } + else if (_headerRenderer != null) + { + Control.TableHeaderView = null; + var platform = _headerRenderer.Element.Platform as Platform; + if (platform != null) + platform.DisposeModelAndChildrenRenderers(_headerRenderer.Element); + _headerRenderer.Dispose(); + _headerRenderer = null; + } + } + + void UpdateIsRefreshing() + { + var refreshing = Element.IsRefreshing; + if (_tableViewController != null) + _tableViewController.UpdateIsRefreshing(refreshing); + } + + void UpdateItems(NotifyCollectionChangedEventArgs e, int section, bool resetWhenGrouped) + { + var exArgs = e as NotifyCollectionChangedEventArgsEx; + if (exArgs != null) + _dataSource.Counts[section] = exArgs.Count; + + var groupReset = resetWhenGrouped && Element.IsGroupingEnabled; + + switch (e.Action) + { + case NotifyCollectionChangedAction.Add: + UpdateEstimatedRowHeight(); + if (e.NewStartingIndex == -1 || groupReset) + goto case NotifyCollectionChangedAction.Reset; + Control.BeginUpdates(); + Control.InsertRows(GetPaths(section, e.NewStartingIndex, e.NewItems.Count), UITableViewRowAnimation.Automatic); + + Control.EndUpdates(); + + break; + + case NotifyCollectionChangedAction.Remove: + if (e.OldStartingIndex == -1 || groupReset) + goto case NotifyCollectionChangedAction.Reset; + Control.BeginUpdates(); + Control.DeleteRows(GetPaths(section, e.OldStartingIndex, e.OldItems.Count), UITableViewRowAnimation.Automatic); + + Control.EndUpdates(); + + if (_estimatedRowHeight && Element.TemplatedItems.Count == 0) + _estimatedRowHeight = false; + + break; + + case NotifyCollectionChangedAction.Move: + if (e.OldStartingIndex == -1 || e.NewStartingIndex == -1 || groupReset) + goto case NotifyCollectionChangedAction.Reset; + Control.BeginUpdates(); + for (var i = 0; i < e.OldItems.Count; i++) + { + var oldi = e.OldStartingIndex; + var newi = e.NewStartingIndex; + + if (e.NewStartingIndex < e.OldStartingIndex) + { + oldi += i; + newi += i; + } + + Control.MoveRow(NSIndexPath.FromRowSection(oldi, section), NSIndexPath.FromRowSection(newi, section)); + } + Control.EndUpdates(); + + if (_estimatedRowHeight && e.OldStartingIndex == 0) + _estimatedRowHeight = false; + + break; + + case NotifyCollectionChangedAction.Replace: + if (e.OldStartingIndex == -1 || groupReset) + goto case NotifyCollectionChangedAction.Reset; + Control.BeginUpdates(); + Control.ReloadRows(GetPaths(section, e.OldStartingIndex, e.OldItems.Count), UITableViewRowAnimation.Automatic); + Control.EndUpdates(); + + if (_estimatedRowHeight && e.OldStartingIndex == 0) + _estimatedRowHeight = false; + + break; + + case NotifyCollectionChangedAction.Reset: + _estimatedRowHeight = false; + Control.ReloadData(); + return; + } + } + + void UpdatePullToRefreshEnabled() + { + if (_tableViewController != null) + { + var isPullToRequestEnabled = Element.IsPullToRefreshEnabled && (Element as IListViewController).RefreshAllowed; + _tableViewController.UpdatePullToRefreshEnabled(isPullToRequestEnabled); + } + } + + void UpdateRowHeight() + { + var rowHeight = Element.RowHeight; + if (Element.HasUnevenRows && rowHeight == -1 && Forms.IsiOS7OrNewer) + { + if (Forms.IsiOS8OrNewer) + Control.RowHeight = UITableView.AutomaticDimension; + } + else + Control.RowHeight = rowHeight <= 0 ? DefaultRowHeight : rowHeight; + } + + void UpdateSeparatorColor() + { + var color = Element.SeparatorColor; + // ...and Steve said to the unbelievers the separator shall be gray, and gray it was. The unbelievers looked on, and saw that it was good, and + // they went forth and documented the default color. The holy scripture still reflects this default. + // Defined here: https://developer.apple.com/library/ios/documentation/UIKit/Reference/UITableView_Class/#//apple_ref/occ/instp/UITableView/separatorColor + Control.SeparatorColor = color.ToUIColor(UIColor.Gray); + } + + void UpdateSeparatorVisibility() + { + var visibility = Element.SeparatorVisibility; + switch (visibility) + { + case SeparatorVisibility.Default: + Control.SeparatorStyle = UITableViewCellSeparatorStyle.SingleLine; + break; + case SeparatorVisibility.None: + Control.SeparatorStyle = UITableViewCellSeparatorStyle.None; + break; + default: + throw new ArgumentOutOfRangeException(); + } + } + + internal class UnevenListViewDataSource : ListViewDataSource + { + IVisualElementRenderer _prototype; + + public UnevenListViewDataSource(ListView list, FormsUITableViewController uiTableViewController) : base(list, uiTableViewController) + { + } + + public UnevenListViewDataSource(ListViewDataSource source) : base(source) + { + } + + public override nfloat GetHeightForRow(UITableView tableView, NSIndexPath indexPath) + { + var cell = GetCellForPath(indexPath); + + if (List.RowHeight == -1 && cell.Height == -1 && cell is ViewCell) + { + // only doing ViewCell because its the only one that matters (the others dont adjust ANYWAY) + if (Forms.IsiOS8OrNewer) + return UITableView.AutomaticDimension; + return CalculateHeightForCell(tableView, cell); + } + + var renderHeight = cell.RenderHeight; + return renderHeight > 0 ? (nfloat)renderHeight : DefaultRowHeight; + } + + internal nfloat CalculateHeightForCell(UITableView tableView, Cell cell) + { + var viewCell = cell as ViewCell; + if (viewCell != null && viewCell.View != null) + { + var target = viewCell.View; + if (_prototype == null) + { + _prototype = Platform.CreateRenderer(target); + Platform.SetRenderer(target, _prototype); + } + else + { + _prototype.SetElement(target); + Platform.SetRenderer(target, _prototype); + } + + var req = target.Measure(tableView.Frame.Width, double.PositiveInfinity, MeasureFlags.IncludeMargins); + + target.ClearValue(Platform.RendererProperty); + foreach (var descendant in target.Descendants()) + descendant.ClearValue(Platform.RendererProperty); + + return (nfloat)req.Request.Height; + } + var renderHeight = cell.RenderHeight; + return renderHeight > 0 ? (nfloat)renderHeight : DefaultRowHeight; + } + } + + internal class ListViewDataSource : UITableViewSource + { + const int DefaultItemTemplateId = 1; + static int s_dataTemplateIncrementer = 2; // lets start at not 0 because + readonly nfloat _defaultSectionHeight; + readonly Dictionary<DataTemplate, int> _templateToId = new Dictionary<DataTemplate, int>(); + readonly UITableView _uiTableView; + readonly FormsUITableViewController _uiTableViewController; + protected readonly ListView List; + bool _isDragging; + bool _selectionFromNative; + + public ListViewDataSource(ListViewDataSource source) + { + _uiTableViewController = source._uiTableViewController; + List = source.List; + _uiTableView = source._uiTableView; + _defaultSectionHeight = source._defaultSectionHeight; + _selectionFromNative = source._selectionFromNative; + + Counts = new Dictionary<int, int>(); + } + + public ListViewDataSource(ListView list, FormsUITableViewController uiTableViewController) + { + _uiTableViewController = uiTableViewController; + _uiTableView = uiTableViewController.TableView; + _defaultSectionHeight = Forms.IsiOS8OrNewer ? DefaultRowHeight : _uiTableView.SectionHeaderHeight; + List = list; + List.ItemSelected += OnItemSelected; + UpdateShortNameListener(); + + Counts = new Dictionary<int, int>(); + } + + public Dictionary<int, int> Counts { get; set; } + + UIColor DefaultBackgroundColor + { + get { return UIColor.Clear; } + } + + public override void DraggingEnded(UIScrollView scrollView, bool willDecelerate) + { + _isDragging = false; + _uiTableViewController.UpdateShowHideRefresh(false); + } + + public override void DraggingStarted(UIScrollView scrollView) + { + _isDragging = true; + } + + public override UITableViewCell GetCell(UITableView tableView, NSIndexPath indexPath) + { + UITableViewCell nativeCell = null; + + var cachingStrategy = List.CachingStrategy; + if (cachingStrategy == ListViewCachingStrategy.RetainElement) + { + var cell = GetCellForPath(indexPath); + nativeCell = CellTableViewCell.GetNativeCell(tableView, cell); + } + else if (cachingStrategy == ListViewCachingStrategy.RecycleElement) + { + var id = TemplateIdForPath(indexPath); + nativeCell = tableView.DequeueReusableCell(ContextActionsCell.Key + id); + if (nativeCell == null) + { + var cell = GetCellForPath(indexPath); + nativeCell = CellTableViewCell.GetNativeCell(tableView, cell, true, id.ToString()); + } + else + { + var templatedList = List.TemplatedItems.GetGroup(indexPath.Section); + var cell = (Cell)((INativeElementView)nativeCell).Element; + cell.SendDisappearing(); + templatedList.UpdateContent(cell, indexPath.Row); + cell.SendAppearing(); + } + } + else + throw new NotSupportedException(); + + var bgColor = tableView.IndexPathForSelectedRow != null && tableView.IndexPathForSelectedRow.Equals(indexPath) ? UIColor.Clear : DefaultBackgroundColor; + + SetCellBackgroundColor(nativeCell, bgColor); + + return nativeCell; + } + + public override nfloat GetHeightForHeader(UITableView tableView, nint section) + { + if (List.IsGroupingEnabled) + { + var cell = List.TemplatedItems[(int)section]; + nfloat height = (float)cell.RenderHeight; + if (height == -1) + height = _defaultSectionHeight; + + return height; + } + + return 0; + } + + public override UIView GetViewForHeader(UITableView tableView, nint section) + { + if (List.IsGroupingEnabled && List.GroupHeaderTemplate != null) + { + var cell = List.TemplatedItems[(int)section]; + if (cell.HasContextActions) + throw new NotSupportedException("Header cells do not support context actions"); + + var renderer = (CellRenderer)Registrar.Registered.GetHandler(cell.GetType()); + + var view = new HeaderWrapperView(); + view.AddSubview(renderer.GetCell(cell, null, tableView)); + + return view; + } + + return null; + } + + public override nint NumberOfSections(UITableView tableView) + { + if (List.IsGroupingEnabled) + return List.TemplatedItems.Count; + + return 1; + } + + public void OnItemSelected(object sender, SelectedItemChangedEventArgs eventArg) + { + if (_selectionFromNative) + { + _selectionFromNative = false; + return; + } + + var location = List.TemplatedItems.GetGroupAndIndexOfItem(eventArg.SelectedItem); + if (location.Item1 == -1 || location.Item2 == -1) + { + var selectedIndexPath = _uiTableView.IndexPathForSelectedRow; + + var animate = true; + + if (selectedIndexPath != null) + { + var cell = _uiTableView.CellAt(selectedIndexPath) as ContextActionsCell; + if (cell != null) + { + cell.PrepareForDeselect(); + if (cell.IsOpen) + animate = false; + } + } + + if (selectedIndexPath != null) + _uiTableView.DeselectRow(selectedIndexPath, animate); + return; + } + + _uiTableView.SelectRow(NSIndexPath.FromRowSection(location.Item2, location.Item1), true, UITableViewScrollPosition.Middle); + } + + public override void RowDeselected(UITableView tableView, NSIndexPath indexPath) + { + var cell = tableView.CellAt(indexPath); + if (cell == null) + return; + + SetCellBackgroundColor(cell, DefaultBackgroundColor); + } + + public override void RowSelected(UITableView tableView, NSIndexPath indexPath) + { + var cell = tableView.CellAt(indexPath); + + if (cell == null) + return; + + Cell formsCell = null; + if (List.CachingStrategy == ListViewCachingStrategy.RecycleElement) + formsCell = (Cell)((INativeElementView)cell).Element; + + SetCellBackgroundColor(cell, UIColor.Clear); + + _selectionFromNative = true; + + tableView.EndEditing(true); + List.NotifyRowTapped(indexPath.Section, indexPath.Row, formsCell); + } + + public override nint RowsInSection(UITableView tableview, nint section) + { + int countOverride; + if (Counts.TryGetValue((int)section, out countOverride)) + { + Counts.Remove((int)section); + return countOverride; + } + + if (List.IsGroupingEnabled) + { + var group = (IList)((IList)List.TemplatedItems)[(int)section]; + return group.Count; + } + + return List.TemplatedItems.Count; + } + + public override void Scrolled(UIScrollView scrollView) + { + if (_isDragging && scrollView.ContentOffset.Y < 0) + _uiTableViewController.UpdateShowHideRefresh(true); + } + + public override string[] SectionIndexTitles(UITableView tableView) + { + if (List.TemplatedItems.ShortNames == null) + return null; + + return List.TemplatedItems.ShortNames.ToArray(); + } + + public override string TitleForHeader(UITableView tableView, nint section) + { + if (!List.IsGroupingEnabled) + return null; + + var sl = GetSectionList((int)section); + sl.PropertyChanged -= OnSectionPropertyChanged; + sl.PropertyChanged += OnSectionPropertyChanged; + + return sl.Name; + } + + public void UpdateGrouping() + { + UpdateShortNameListener(); + _uiTableView.ReloadData(); + } + + protected Cell GetCellForPath(NSIndexPath indexPath) + { + var templatedList = List.TemplatedItems; + if (List.IsGroupingEnabled) + templatedList = (TemplatedItemsList<ItemsView<Cell>, Cell>)((IList)templatedList)[indexPath.Section]; + + var cell = templatedList[indexPath.Row]; + return cell; + } + + TemplatedItemsList<ItemsView<Cell>, Cell> GetSectionList(int section) + { + return (TemplatedItemsList<ItemsView<Cell>, Cell>)((IList)List.TemplatedItems)[section]; + } + + void OnSectionPropertyChanged(object sender, PropertyChangedEventArgs e) + { + var currentSelected = _uiTableView.IndexPathForSelectedRow; + + var til = (TemplatedItemsList<ItemsView<Cell>, Cell>)sender; + var groupIndex = ((IList)List.TemplatedItems).IndexOf(til); + if (groupIndex == -1) + { + til.PropertyChanged -= OnSectionPropertyChanged; + return; + } + + _uiTableView.ReloadSections(NSIndexSet.FromIndex(groupIndex), UITableViewRowAnimation.Automatic); + _uiTableView.SelectRow(currentSelected, false, UITableViewScrollPosition.None); + } + + void OnShortNamesCollectionChanged(object sender, NotifyCollectionChangedEventArgs e) + { + _uiTableView.ReloadSectionIndexTitles(); + } + + static void SetCellBackgroundColor(UITableViewCell cell, UIColor color) + { + var contextCell = cell as ContextActionsCell; + cell.BackgroundColor = color; + if (contextCell != null) + contextCell.ContentCell.BackgroundColor = color; + } + + int TemplateIdForPath(NSIndexPath indexPath) + { + var itemTemplate = List.ItemTemplate; + var selector = itemTemplate as DataTemplateSelector; + if (selector == null) + return DefaultItemTemplateId; + + var templatedList = List.TemplatedItems; + if (List.IsGroupingEnabled) + templatedList = (TemplatedItemsList<ItemsView<Cell>, Cell>)((IList)templatedList)[indexPath.Section]; + + var item = templatedList.ListProxy[indexPath.Row]; + + itemTemplate = selector.SelectTemplate(item, List); + int key; + if (!_templateToId.TryGetValue(itemTemplate, out key)) + { + s_dataTemplateIncrementer++; + key = s_dataTemplateIncrementer; + _templateToId[itemTemplate] = key; + } + return key; + } + + void UpdateShortNameListener() + { + if (List.IsGroupingEnabled) + { + if (List.TemplatedItems.ShortNames != null) + ((INotifyCollectionChanged)List.TemplatedItems.ShortNames).CollectionChanged += OnShortNamesCollectionChanged; + } + else + { + if (List.TemplatedItems.ShortNames != null) + ((INotifyCollectionChanged)List.TemplatedItems.ShortNames).CollectionChanged -= OnShortNamesCollectionChanged; + } + } + } + } + + internal class HeaderWrapperView : UIView + { + public override void LayoutSubviews() + { + base.LayoutSubviews(); + foreach (var item in Subviews) + item.Frame = Bounds; + } + } + + internal class FormsUITableViewController : UITableViewController + { + readonly ListView _list; + UIRefreshControl _refresh; + + bool _refreshAdded; + + public FormsUITableViewController(ListView element) + { + if (Forms.IsiOS9OrNewer) + TableView.CellLayoutMarginsFollowReadableWidth = false; + _refresh = new UIRefreshControl(); + _refresh.ValueChanged += OnRefreshingChanged; + _list = element; + } + + public void UpdateIsRefreshing(bool refreshing) + { + if (refreshing) + { + if (!_refreshAdded) + { + RefreshControl = _refresh; + _refreshAdded = true; + } + + if (!_refresh.Refreshing) + { + _refresh.BeginRefreshing(); + + //hack: when we don't have cells in our UITableView the spinner fails to appear + CheckContentSize(); + + TableView.ScrollRectToVisible(new RectangleF(0, 0, _refresh.Bounds.Width, _refresh.Bounds.Height), true); + } + } + else + { + _refresh.EndRefreshing(); + + if (!_list.IsPullToRefreshEnabled) + RemoveRefresh(); + } + } + + public void UpdatePullToRefreshEnabled(bool pullToRefreshEnabled) + { + if (pullToRefreshEnabled) + { + if (!_refreshAdded) + { + _refreshAdded = true; + RefreshControl = _refresh; + } + } + else if (_refreshAdded) + { + if (_refresh.Refreshing) + _refresh.EndRefreshing(); + + RefreshControl = null; + _refreshAdded = false; + } + } + + public void UpdateShowHideRefresh(bool shouldHide) + { + if (_list.IsPullToRefreshEnabled) + return; + + if (shouldHide) + RemoveRefresh(); + else + UpdateIsRefreshing(_list.IsRefreshing); + } + + public override void ViewWillAppear(bool animated) + { + } + + protected override void Dispose(bool disposing) + { + base.Dispose(disposing); + + if (disposing && _refresh != null) + { + _refresh.ValueChanged -= OnRefreshingChanged; + _refresh.EndRefreshing(); + _refresh.Dispose(); + _refresh = null; + } + } + + void CheckContentSize() + { + //adding a default height of at least 1 pixel tricks iOS to show the spinner + var contentSize = TableView.ContentSize; + if (contentSize.Height == 0) + TableView.ContentSize = new SizeF(contentSize.Width, 1); + } + + void OnRefreshingChanged(object sender, EventArgs eventArgs) + { + if (_refresh.Refreshing) + (_list as IListViewController).SendRefreshing(); + } + + void RemoveRefresh() + { + if (!_refreshAdded) + return; + + if (_refresh.Refreshing) + _refresh.EndRefreshing(); + + RefreshControl = null; + _refreshAdded = false; + } + } +}
\ No newline at end of file diff --git a/Xamarin.Forms.Platform.iOS/Renderers/NavigationMenuRenderer.cs b/Xamarin.Forms.Platform.iOS/Renderers/NavigationMenuRenderer.cs new file mode 100644 index 00000000..7136a61d --- /dev/null +++ b/Xamarin.Forms.Platform.iOS/Renderers/NavigationMenuRenderer.cs @@ -0,0 +1,152 @@ +using System; +using System.Drawing; +using System.Linq; +#if __UNIFIED__ +using UIKit; +using Foundation; +#else +using MonoTouch.UIKit; +using MonoTouch.Foundation; +#endif +#if __UNIFIED__ +using RectangleF = CoreGraphics.CGRect; +using SizeF = CoreGraphics.CGSize; +using PointF = CoreGraphics.CGPoint; + +#else +using nfloat=System.Single; +using nint=System.Int32; +using nuint=System.UInt32; +#endif + +namespace Xamarin.Forms.Platform.iOS +{ + public class NavigationMenuRenderer : ViewRenderer + { + UICollectionView _collectionView; + + protected override void OnElementChanged(ElementChangedEventArgs<View> e) + { + base.OnElementChanged(e); + var pad = UIDevice.CurrentDevice.UserInterfaceIdiom == UIUserInterfaceIdiom.Pad; + var size = pad ? 220 : 142; + var margin = pad ? 27 : 12; + var bottomMargin = (int)(margin * 0.8); + + _collectionView = new UICollectionView(new RectangleF(0, 0, 100, 100), + new UICollectionViewFlowLayout + { + ItemSize = new SizeF(size, size + 30), + ScrollDirection = UICollectionViewScrollDirection.Vertical, + SectionInset = new UIEdgeInsets(margin, margin, bottomMargin, margin), + MinimumInteritemSpacing = margin, + MinimumLineSpacing = margin + }) { DataSource = new DataSource((NavigationMenu)Element), BackgroundColor = UIColor.White }; + + using(var navigationCellId = new NSString("NavigationCell")) + _collectionView.RegisterClassForCell(typeof(NavigationCell), navigationCellId); + + SetNativeControl(_collectionView); + } + + sealed class NavigationCell : UICollectionViewCell + { + readonly UIButton _image = new UIButton(RectangleF.Empty); + + readonly UILabel _nameLabel = new UILabel(RectangleF.Empty) { BackgroundColor = UIColor.Clear, TextAlignment = UITextAlignment.Center, Font = UIFont.SystemFontOfSize(14) }; + + string _icon; + + [Export("initWithFrame:")] + public NavigationCell(RectangleF frame) : base(frame) + { + SetupLayer(); + _image.TouchUpInside += (object sender, EventArgs e) => + { + if (Selected != null) + Selected(); + }; + _image.ContentMode = UIViewContentMode.ScaleAspectFit; + _image.Center = ContentView.Center; + + ContentView.AddSubview(_image); + ContentView.AddSubview(_nameLabel); + } + + public string Icon + { + get { return _icon; } + set + { + _icon = value; + _image.SetImage(new UIImage(_icon), UIControlState.Normal); + } + } + + public string Name + { + get { return _nameLabel.Text; } + set { _nameLabel.Text = value; } + } + + public Action Selected { get; set; } + + public override void LayoutSubviews() + { + base.LayoutSubviews(); + _image.Frame = new RectangleF(0, 0, ContentView.Frame.Width, ContentView.Frame.Height - 30); + var sizeThatFits = _nameLabel.SizeThatFits(ContentView.Frame.Size); + _nameLabel.Frame = new RectangleF(0, ContentView.Frame.Height - 15 - sizeThatFits.Height / 2, ContentView.Frame.Width, sizeThatFits.Height); + } + + void SetupLayer() + { + var layer = _image.Layer; + + layer.ShadowRadius = 6; + layer.ShadowColor = UIColor.Black.CGColor; + layer.ShadowOpacity = 0.2f; + layer.ShadowOffset = new SizeF(); + + layer.RasterizationScale = UIScreen.MainScreen.Scale; + layer.ShouldRasterize = true; + } + } + + class DataSource : UICollectionViewDataSource + { + readonly NavigationMenu _menu; + + public DataSource(NavigationMenu menu) + { + _menu = menu; + } + + public override UICollectionViewCell GetCell(UICollectionView collectionView, NSIndexPath indexPath) + { + var cell = (NavigationCell)collectionView.DequeueReusableCell(new NSString("NavigationCell"), indexPath); + var target = _menu.Targets.Skip(indexPath.Row).FirstOrDefault(); + + if (target != null) + { + cell.Name = target.Title; + cell.Icon = target.Icon; + cell.Selected = () => _menu.SendTargetSelected(target); + } + else + { + cell.Selected = null; + cell.Icon = ""; + cell.Name = ""; + } + + return cell; + } + + public override nint GetItemsCount(UICollectionView collectionView, nint section) + { + return _menu.Targets.Count(); + } + } + } +}
\ No newline at end of file diff --git a/Xamarin.Forms.Platform.iOS/Renderers/NavigationRenderer.cs b/Xamarin.Forms.Platform.iOS/Renderers/NavigationRenderer.cs new file mode 100644 index 00000000..63330564 --- /dev/null +++ b/Xamarin.Forms.Platform.iOS/Renderers/NavigationRenderer.cs @@ -0,0 +1,918 @@ +using System; +using System.Collections.Generic; +using System.ComponentModel; +using System.Diagnostics; +using System.Drawing; +using System.Linq; +using System.Threading; +using System.Threading.Tasks; +#if __UNIFIED__ +using UIKit; +using CoreGraphics; +#else +using MonoTouch.UIKit; +using MonoTouch.CoreGraphics; +#endif +#if __UNIFIED__ +using RectangleF = CoreGraphics.CGRect; +using SizeF = CoreGraphics.CGSize; +using PointF = CoreGraphics.CGPoint; + +#else +using nfloat=System.Single; +using nint=System.Int32; +using nuint=System.UInt32; +#endif + +namespace Xamarin.Forms.Platform.iOS +{ + public class NavigationRenderer : UINavigationController, IVisualElementRenderer + { + internal const string UpdateToolbarButtons = "Xamarin.UpdateToolbarButtons"; + bool _appeared; + bool _ignorePopCall; + bool _loaded; + MasterDetailPage _parentMasterDetailPage; + Size _queuedSize; + UIViewController[] _removeControllers; + UIToolbar _secondaryToolbar; + + VisualElementTracker _tracker; + + public NavigationRenderer() + { + MessagingCenter.Subscribe<IVisualElementRenderer>(this, UpdateToolbarButtons, sender => + { + if (!ViewControllers.Any()) + return; + var parentingViewController = (ParentingViewController)ViewControllers.Last(); + UpdateLeftBarButtonItem(parentingViewController); + }); + } + + Page Current { get; set; } + + public VisualElement Element { get; private set; } + + public event EventHandler<VisualElementChangedEventArgs> ElementChanged; + + public SizeRequest GetDesiredSize(double widthConstraint, double heightConstraint) + { + return NativeView.GetSizeRequest(widthConstraint, heightConstraint); + } + + public UIView NativeView + { + get { return View; } + } + + public void SetElement(VisualElement element) + { + var oldElement = Element; + Element = (NavigationPage)element; + OnElementChanged(new VisualElementChangedEventArgs(oldElement, element)); + + if (element != null) + element.SendViewInitialized(NativeView); + } + + public void SetElementSize(Size size) + { + if (_loaded) + Element.Layout(new Rectangle(Element.X, Element.Y, size.Width, size.Height)); + else + _queuedSize = size; + } + + public UIViewController ViewController + { + get { return this; } + } + + public override void DidRotate(UIInterfaceOrientation fromInterfaceOrientation) + { + base.DidRotate(fromInterfaceOrientation); + + View.SetNeedsLayout(); + + var parentingViewController = (ParentingViewController)ViewControllers.Last(); + UpdateLeftBarButtonItem(parentingViewController); + } + + public Task<bool> PopToRootAsync(Page page, bool animated = true) + { + return OnPopToRoot(page, animated); + } + + public override UIViewController[] PopToRootViewController(bool animated) + { + if (!_ignorePopCall && ViewControllers.Length > 1) + RemoveViewControllers(animated); + + return base.PopToRootViewController(animated); + } + + public Task<bool> PopViewAsync(Page page, bool animated = true) + { + return OnPopViewAsync(page, animated); + } + +#if __UNIFIED__ + public override UIViewController PopViewController(bool animated) +#else + public override UIViewController PopViewControllerAnimated (bool animated) + #endif + { + RemoveViewControllers(animated); +#if __UNIFIED__ + return base.PopViewController(animated); +#else + return base.PopViewControllerAnimated (animated); + #endif + } + + public Task<bool> PushPageAsync(Page page, bool animated = true) + { + return OnPushAsync(page, animated); + } + + public override void ViewDidAppear(bool animated) + { + if (!_appeared) + { + _appeared = true; + ((NavigationPage)Element)?.SendAppearing(); + } + + base.ViewDidAppear(animated); + + View.SetNeedsLayout(); + } + + public override void ViewDidDisappear(bool animated) + { + base.ViewDidDisappear(animated); + + if (!_appeared || Element == null) + return; + + _appeared = false; + ((NavigationPage)Element).SendDisappearing(); + } + + public override void ViewDidLayoutSubviews() + { + base.ViewDidLayoutSubviews(); + UpdateToolBarVisible(); + + var navBarFrame = NavigationBar.Frame; + + var toolbar = _secondaryToolbar; + // Use 0 if the NavBar is hidden or will be hidden + var toolbarY = NavigationBarHidden || !NavigationPage.GetHasNavigationBar(Current) ? 0 : navBarFrame.Bottom; + toolbar.Frame = new RectangleF(0, toolbarY, View.Frame.Width, toolbar.Frame.Height); + + double trueBottom = toolbar.Hidden ? toolbarY : toolbar.Frame.Bottom; + var modelSize = _queuedSize.IsZero ? Element.Bounds.Size : _queuedSize; + ((NavigationPage)Element).ContainerArea = new Rectangle(0, toolbar.Hidden ? 0 : toolbar.Frame.Height, modelSize.Width, modelSize.Height - trueBottom); + + if (!_queuedSize.IsZero) + { + Element.Layout(new Rectangle(Element.X, Element.Y, _queuedSize.Width, _queuedSize.Height)); + _queuedSize = Size.Zero; + } + + _loaded = true; + + foreach (var view in View.Subviews) + { + if (view == NavigationBar || view == _secondaryToolbar) + continue; + view.Frame = View.Bounds; + } + } + + public override void ViewDidLoad() + { + base.ViewDidLoad(); + + if (Forms.IsiOS7OrNewer) + NavigationBar.Translucent = false; + else + WantsFullScreenLayout = false; + + _secondaryToolbar = new SecondaryToolbar { Frame = new RectangleF(0, 0, 320, 44) }; + View.Add(_secondaryToolbar); + _secondaryToolbar.Hidden = true; + + FindParentMasterDetail(); + + var navPage = (NavigationPage)Element; + + if (navPage.CurrentPage == null) + { + throw new InvalidOperationException("NavigationPage must have a root Page before being used. Either call PushAsync with a valid Page, or pass a Page to the constructor before usage."); + } + + navPage.PushRequested += OnPushRequested; + navPage.PopRequested += OnPopRequested; + navPage.PopToRootRequested += OnPopToRootRequested; + navPage.RemovePageRequested += OnRemovedPageRequested; + navPage.InsertPageBeforeRequested += OnInsertPageBeforeRequested; + + UpdateTint(); + UpdateBarBackgroundColor(); + UpdateBarTextColor(); + + // If there is already stuff on the stack we need to push it + navPage.StackCopy.Reverse().ForEach(async p => await PushPageAsync(p, false)); + + _tracker = new VisualElementTracker(this); + + Element.PropertyChanged += HandlePropertyChanged; + + UpdateToolBarVisible(); + UpdateBackgroundColor(); + Current = navPage.CurrentPage; + } + + protected override void Dispose(bool disposing) + { + if (disposing) + { + MessagingCenter.Unsubscribe<IVisualElementRenderer>(this, UpdateToolbarButtons); + + foreach (var childViewController in ViewControllers) + childViewController.Dispose(); + + if (_tracker != null) + _tracker.Dispose(); + + _secondaryToolbar.RemoveFromSuperview(); + _secondaryToolbar.Dispose(); + _secondaryToolbar = null; + + _parentMasterDetailPage = null; + Current = null; // unhooks events + + var navPage = (NavigationPage)Element; + navPage.PropertyChanged -= HandlePropertyChanged; + navPage.PushRequested -= OnPushRequested; + navPage.PopRequested -= OnPopRequested; + navPage.PopToRootRequested -= OnPopToRootRequested; + navPage.RemovePageRequested -= OnRemovedPageRequested; + navPage.InsertPageBeforeRequested -= OnInsertPageBeforeRequested; + } + + base.Dispose(disposing); + if (_appeared) + { + ((Page)Element).SendDisappearing(); + + _appeared = false; + } + } + + protected virtual void OnElementChanged(VisualElementChangedEventArgs e) + { + var changed = ElementChanged; + if (changed != null) + changed(this, e); + } + + protected virtual async Task<bool> OnPopToRoot(Page page, bool animated) + { + _ignorePopCall = true; + var renderer = Platform.GetRenderer(page); + if (renderer == null || renderer.ViewController == null) + return false; + + var task = GetAppearedOrDisappearedTask(page); + + PopToRootViewController(animated); + + _ignorePopCall = false; + var success = !await task; + + UpdateToolBarVisible(); + return success; + } + + protected virtual async Task<bool> OnPopViewAsync(Page page, bool animated) + { + if (_ignorePopCall) + return true; + + var renderer = Platform.GetRenderer(page); + if (renderer == null || renderer.ViewController == null) + return false; + + var actuallyRemoved = false; + + if (page != ((ParentingViewController)TopViewController).Child) + throw new NotSupportedException("Popped page does not appear on top of current navigation stack, please file a bug."); + + var task = GetAppearedOrDisappearedTask(page); + + UIViewController poppedViewController; +#if __UNIFIED__ + poppedViewController = base.PopViewController(animated); +#else + poppedViewController = base.PopViewControllerAnimated (animated); + #endif + + if (poppedViewController == null) + { + // this happens only when the user does something REALLY dumb like pop right after putting the page as visible. + poppedViewController = TopViewController; + var newControllers = ViewControllers.Remove(poppedViewController); + ViewControllers = newControllers; + actuallyRemoved = true; + } + else + actuallyRemoved = !await task; + + poppedViewController.Dispose(); + + UpdateToolBarVisible(); + return actuallyRemoved; + } + + protected virtual async Task<bool> OnPushAsync(Page page, bool animated) + { + var pack = CreateViewControllerForPage(page); + var task = GetAppearedOrDisappearedTask(page); + + PushViewController(pack, animated); + + var shown = await task; + UpdateToolBarVisible(); + return shown; + } + + ParentingViewController CreateViewControllerForPage(Page page) + { + if (Platform.GetRenderer(page) == null) + Platform.SetRenderer(page, Platform.CreateRenderer(page)); + + // must pack into container so padding can work + // otherwise the view controller is forced to 0,0 + var pack = new ParentingViewController(this) { Child = page }; + if (!string.IsNullOrWhiteSpace(page.Title)) + pack.NavigationItem.Title = page.Title; + + // First page and we have a master detail to contend with + UpdateLeftBarButtonItem(pack); + + //var pack = Platform.GetRenderer (view).ViewController; + + var titleIcon = NavigationPage.GetTitleIcon(page); + if (!string.IsNullOrEmpty(titleIcon)) + { + try + { + //UIImage ctor throws on file not found if MonoTouch.ObjCRuntime.Class.ThrowOnInitFailure is true; + pack.NavigationItem.TitleView = new UIImageView(new UIImage(titleIcon)); + } + catch + { + } + } + + var titleText = NavigationPage.GetBackButtonTitle(page); + if (titleText != null) + { + pack.NavigationItem.BackBarButtonItem = new UIBarButtonItem(titleText, UIBarButtonItemStyle.Plain, async (o, e) => await PopViewAsync(page)); + } + + var pageRenderer = Platform.GetRenderer(page); + pack.View.AddSubview(pageRenderer.ViewController.View); + pack.AddChildViewController(pageRenderer.ViewController); + pageRenderer.ViewController.DidMoveToParentViewController(pack); + + return pack; + } + + void FindParentMasterDetail() + { + var parentPages = ((Page)Element).GetParentPages(); + var masterDetail = parentPages.OfType<MasterDetailPage>().FirstOrDefault(); + + if (masterDetail != null && parentPages.Append((Page)Element).Contains(masterDetail.Detail)) + _parentMasterDetailPage = masterDetail; + } + + Task<bool> GetAppearedOrDisappearedTask(Page page) + { + var tcs = new TaskCompletionSource<bool>(); + + var parentViewController = Platform.GetRenderer(page).ViewController.ParentViewController as ParentingViewController; + if (parentViewController == null) + throw new NotSupportedException("ParentingViewController parent could not be found. Please file a bug."); + + EventHandler appearing = null, disappearing = null; + appearing = (s, e) => + { + parentViewController.Appearing -= appearing; + parentViewController.Disappearing -= disappearing; + + Device.BeginInvokeOnMainThread(() => { tcs.SetResult(true); }); + }; + + disappearing = (s, e) => + { + parentViewController.Appearing -= appearing; + parentViewController.Disappearing -= disappearing; + + Device.BeginInvokeOnMainThread(() => { tcs.SetResult(false); }); + }; + + parentViewController.Appearing += appearing; + parentViewController.Disappearing += disappearing; + + return tcs.Task; + } + + void HandlePropertyChanged(object sender, PropertyChangedEventArgs e) + { + if (e.PropertyName == NavigationPage.TintProperty.PropertyName) + UpdateTint(); + if (e.PropertyName == NavigationPage.BarBackgroundColorProperty.PropertyName) + UpdateBarBackgroundColor(); + else if (e.PropertyName == NavigationPage.BarTextColorProperty.PropertyName) + UpdateBarTextColor(); + else if (e.PropertyName == VisualElement.BackgroundColorProperty.PropertyName) + UpdateBackgroundColor(); + else if (e.PropertyName == NavigationPage.CurrentPageProperty.PropertyName) + Current = ((NavigationPage)Element).CurrentPage; + } + + void InsertPageBefore(Page page, Page before) + { + if (before == null) + throw new ArgumentNullException("before"); + if (page == null) + throw new ArgumentNullException("page"); + + var pageContainer = CreateViewControllerForPage(page); + var target = Platform.GetRenderer(before).ViewController.ParentViewController; + ViewControllers = ViewControllers.Insert(ViewControllers.IndexOf(target), pageContainer); + } + + void OnInsertPageBeforeRequested(object sender, NavigationRequestedEventArgs e) + { + InsertPageBefore(e.Page, e.BeforePage); + } + + void OnPopRequested(object sender, NavigationRequestedEventArgs e) + { + e.Task = PopViewAsync(e.Page, e.Animated); + } + + void OnPopToRootRequested(object sender, NavigationRequestedEventArgs e) + { + e.Task = PopToRootAsync(e.Page, e.Animated); + } + + void OnPushRequested(object sender, NavigationRequestedEventArgs e) + { + e.Task = PushPageAsync(e.Page, e.Animated); + } + + void OnRemovedPageRequested(object sender, NavigationRequestedEventArgs e) + { + RemovePage(e.Page); + } + + void RemovePage(Page page) + { + if (page == null) + throw new ArgumentNullException("page"); + if (page == Current) + throw new NotSupportedException(); // should never happen as NavPage protecs against this + + var target = Platform.GetRenderer(page).ViewController.ParentViewController; + + // So the ViewControllers property is not very property like on iOS. Assigning to it doesn't cause it to be + // immediately reflected into the property. The change will not be reflected until there has been sufficient time + // to process it (it ends up on the event queue). So to resolve this issue we keep our own stack until we + // know iOS has processed it, and make sure any updates use that. + + // In the future we may want to make RemovePageAsync and deprecate RemovePage to handle cases where Push/Pop is called + // during a remove cycle. + + if (_removeControllers == null) + { + _removeControllers = ViewControllers.Remove(target); + ViewControllers = _removeControllers; + Device.BeginInvokeOnMainThread(() => { _removeControllers = null; }); + } + else + { + _removeControllers = _removeControllers.Remove(target); + ViewControllers = _removeControllers; + } + } + + void RemoveViewControllers(bool animated) + { + var controller = TopViewController as ParentingViewController; + if (controller == null || controller.Child == null) + return; + + // Gesture in progress, lets not be proactive and just wait for it to finish + var count = ViewControllers.Length; + var task = GetAppearedOrDisappearedTask(controller.Child); + task.ContinueWith(async t => + { + // task returns true if the user lets go of the page and is not popped + // however at this point the renderer is already off the visual stack so we just need to update the NavigationPage + // Also worth noting this task returns on the main thread + if (t.Result) + return; + _ignorePopCall = true; + // because iOS will just chain multiple animations together... + var removed = count - ViewControllers.Length; + for (var i = 0; i < removed; i++) + { + // lets just pop these suckers off, do not await, the true is there to make this fast + await ((NavigationPage)Element).PopAsyncInner(animated, true); + } + // because we skip the normal pop process we need to dispose ourselves + controller.Dispose(); + _ignorePopCall = false; + }, TaskScheduler.FromCurrentSynchronizationContext()); + } + + void UpdateBackgroundColor() + { + var color = Element.BackgroundColor == Color.Default ? Color.White : Element.BackgroundColor; + View.BackgroundColor = color.ToUIColor(); + } + + void UpdateBarBackgroundColor() + { + var barBackgroundColor = ((NavigationPage)Element).BarBackgroundColor; + // Set navigation bar background color + if (Forms.IsiOS7OrNewer) + { + NavigationBar.BarTintColor = barBackgroundColor == Color.Default ? UINavigationBar.Appearance.BarTintColor : barBackgroundColor.ToUIColor(); + } + else + { + NavigationBar.TintColor = barBackgroundColor == Color.Default ? UINavigationBar.Appearance.TintColor : barBackgroundColor.ToUIColor(); + } + } + + void UpdateBarTextColor() + { + var barTextColor = ((NavigationPage)Element).BarTextColor; + + var globalAttributes = UINavigationBar.Appearance.GetTitleTextAttributes(); + + if (barTextColor == Color.Default) + { + if (NavigationBar.TitleTextAttributes != null) + { + var attributes = new UIStringAttributes(); + attributes.ForegroundColor = globalAttributes.TextColor; + attributes.Font = globalAttributes.Font; + NavigationBar.TitleTextAttributes = attributes; + } + } + else + { + var titleAttributes = new UIStringAttributes(); + titleAttributes.Font = globalAttributes.Font; + titleAttributes.ForegroundColor = barTextColor == Color.Default ? titleAttributes.ForegroundColor ?? UINavigationBar.Appearance.TintColor : barTextColor.ToUIColor(); + NavigationBar.TitleTextAttributes = titleAttributes; + } + + // set Tint color (i. e. Back Button arrow and Text) + if (Forms.IsiOS7OrNewer) + { + NavigationBar.TintColor = barTextColor == Color.Default ? UINavigationBar.Appearance.TintColor : barTextColor.ToUIColor(); + } + + if (barTextColor.Luminosity > 0.5) + { + // Use light text color for status bar + UIApplication.SharedApplication.StatusBarStyle = UIStatusBarStyle.LightContent; + } + else + { + // Use dark text color for status bar + UIApplication.SharedApplication.StatusBarStyle = UIStatusBarStyle.Default; + } + } + + void UpdateLeftBarButtonItem(ParentingViewController containerController) + { + var currentChild = containerController.Child; + var firstPage = ((NavigationPage)Element).StackCopy.LastOrDefault(); + if ((currentChild != firstPage && NavigationPage.GetHasBackButton(currentChild)) || _parentMasterDetailPage == null) + return; + + if (!_parentMasterDetailPage.ShouldShowToolbarButton()) + { + containerController.NavigationItem.LeftBarButtonItem = null; + return; + } + + var shouldUseIcon = _parentMasterDetailPage.Master.Icon != null; + if (shouldUseIcon) + { + try + { + containerController.NavigationItem.LeftBarButtonItem = new UIBarButtonItem(new UIImage(_parentMasterDetailPage.Master.Icon), UIBarButtonItemStyle.Plain, + (o, e) => _parentMasterDetailPage.IsPresented = !_parentMasterDetailPage.IsPresented); + } + catch (Exception) + { + // Throws Exception otherwise would catch more specific exception type + shouldUseIcon = false; + } + } + + if (!shouldUseIcon) + { + containerController.NavigationItem.LeftBarButtonItem = new UIBarButtonItem(_parentMasterDetailPage.Master.Title, UIBarButtonItemStyle.Plain, + (o, e) => _parentMasterDetailPage.IsPresented = !_parentMasterDetailPage.IsPresented); + } + } + + void UpdateTint() + { + var tintColor = ((NavigationPage)Element).Tint; + + if (Forms.IsiOS7OrNewer) + { + NavigationBar.BarTintColor = tintColor == Color.Default ? UINavigationBar.Appearance.BarTintColor : tintColor.ToUIColor(); + if (tintColor == Color.Default) + NavigationBar.TintColor = UINavigationBar.Appearance.TintColor; + else + NavigationBar.TintColor = tintColor.Luminosity > 0.5 ? UIColor.Black : UIColor.White; + } + else + NavigationBar.TintColor = tintColor == Color.Default ? null : tintColor.ToUIColor(); + } + + void UpdateToolBarVisible() + { + if (_secondaryToolbar == null) + return; + if (TopViewController != null && TopViewController.ToolbarItems != null && TopViewController.ToolbarItems.Any()) + { + _secondaryToolbar.Hidden = false; + _secondaryToolbar.Items = TopViewController.ToolbarItems; + } + else + { + _secondaryToolbar.Hidden = true; + //secondaryToolbar.Items = null; + } + } + + class SecondaryToolbar : UIToolbar + { + readonly List<UIView> _lines = new List<UIView>(); + + public SecondaryToolbar() + { + TintColor = UIColor.White; + } + + public override UIBarButtonItem[] Items + { + get { return base.Items; } + set + { + base.Items = value; + SetupLines(); + } + } + + public override void LayoutSubviews() + { + base.LayoutSubviews(); + if (Items == null || Items.Length == 0) + return; + nfloat padding = 11f; + var itemWidth = (Bounds.Width - padding) / Items.Length - padding; + var x = padding; + var itemH = Bounds.Height - 10; + foreach (var item in Items) + { + var frame = new RectangleF(x, 5, itemWidth, itemH); + item.CustomView.Frame = frame; + x += itemWidth + padding; + } + x = itemWidth + padding * 1.5f; + var y = Bounds.GetMidY(); + foreach (var l in _lines) + { + l.Center = new PointF(x, y); + x += itemWidth + padding; + } + } + + void SetupLines() + { + _lines.ForEach(l => l.RemoveFromSuperview()); + _lines.Clear(); + if (Items == null) + return; + for (var i = 1; i < Items.Length; i++) + { + var l = new UIView(new RectangleF(0, 0, 1, 24)) { BackgroundColor = new UIColor(0, 0, 0, 0.2f) }; + AddSubview(l); + _lines.Add(l); + } + } + } + + class ParentingViewController : UIViewController + { + readonly WeakReference<NavigationRenderer> _navigation; + + Page _child; + ToolbarTracker _tracker = new ToolbarTracker(); + + public ParentingViewController(NavigationRenderer navigation) + { + if (Forms.IsiOS7OrNewer) + AutomaticallyAdjustsScrollViewInsets = false; + + _navigation = new WeakReference<NavigationRenderer>(navigation); + } + + public Page Child + { + get { return _child; } + set + { + if (_child == value) + return; + + if (_child != null) + _child.PropertyChanged -= HandleChildPropertyChanged; + + _child = value; + + if (_child != null) + _child.PropertyChanged += HandleChildPropertyChanged; + + UpdateHasBackButton(); + } + } + + public event EventHandler Appearing; + + public override void DidRotate(UIInterfaceOrientation fromInterfaceOrientation) + { + base.DidRotate(fromInterfaceOrientation); + + View.SetNeedsLayout(); + } + + public event EventHandler Disappearing; + + public override void ViewDidAppear(bool animated) + { + base.ViewDidAppear(animated); + + var handler = Appearing; + if (handler != null) + handler(this, EventArgs.Empty); + } + + public override void ViewDidDisappear(bool animated) + { + base.ViewDidDisappear(animated); + + var handler = Disappearing; + if (handler != null) + handler(this, EventArgs.Empty); + } + + public override void ViewDidLayoutSubviews() + { + IVisualElementRenderer childRenderer; + if (Child != null && (childRenderer = Platform.GetRenderer(Child)) != null) + childRenderer.NativeView.Frame = Child.Bounds.ToRectangleF(); + base.ViewDidLayoutSubviews(); + } + + public override void ViewDidLoad() + { + base.ViewDidLoad(); + + _tracker.Target = Child; + _tracker.AdditionalTargets = Child.GetParentPages(); + _tracker.CollectionChanged += TrackerOnCollectionChanged; + + UpdateToolbarItems(); + } + + public override void ViewWillAppear(bool animated) + { + UpdateNavigationBarVisibility(animated); + base.ViewWillAppear(animated); + } + + protected override void Dispose(bool disposing) + { + if (disposing) + { + Child.SendDisappearing(); + Child = null; + _tracker.Target = null; + _tracker.CollectionChanged -= TrackerOnCollectionChanged; + _tracker = null; + + if (NavigationItem.RightBarButtonItems != null) + { + for (var i = 0; i < NavigationItem.RightBarButtonItems.Length; i++) + NavigationItem.RightBarButtonItems[i].Dispose(); + } + + if (ToolbarItems != null) + { + for (var i = 0; i < ToolbarItems.Length; i++) + ToolbarItems[i].Dispose(); + } + } + base.Dispose(disposing); + } + + void HandleChildPropertyChanged(object sender, PropertyChangedEventArgs e) + { + if (e.PropertyName == NavigationPage.HasNavigationBarProperty.PropertyName) + UpdateNavigationBarVisibility(true); + else if (e.PropertyName == Page.TitleProperty.PropertyName) + NavigationItem.Title = Child.Title; + else if (e.PropertyName == NavigationPage.HasBackButtonProperty.PropertyName) + UpdateHasBackButton(); + } + + void TrackerOnCollectionChanged(object sender, EventArgs eventArgs) + { + UpdateToolbarItems(); + } + + void UpdateHasBackButton() + { + if (Child == null) + return; + + NavigationItem.HidesBackButton = !NavigationPage.GetHasBackButton(Child); + } + + void UpdateNavigationBarVisibility(bool animated) + { + var current = Child; + + if (current == null || NavigationController == null) + return; + + var hasNavBar = NavigationPage.GetHasNavigationBar(current); + + if (NavigationController.NavigationBarHidden == hasNavBar) + NavigationController.SetNavigationBarHidden(!hasNavBar, animated); + } + + void UpdateToolbarItems() + { + if (NavigationItem.RightBarButtonItems != null) + { + for (var i = 0; i < NavigationItem.RightBarButtonItems.Length; i++) + NavigationItem.RightBarButtonItems[i].Dispose(); + } + if (ToolbarItems != null) + { + for (var i = 0; i < ToolbarItems.Length; i++) + ToolbarItems[i].Dispose(); + } + + List<UIBarButtonItem> primaries = null; + List<UIBarButtonItem> secondaries = null; + foreach (var item in _tracker.ToolbarItems) + { + if (item.Order == ToolbarItemOrder.Secondary) + (secondaries = secondaries ?? new List<UIBarButtonItem>()).Add(item.ToUIBarButtonItem(true)); + else + (primaries = primaries ?? new List<UIBarButtonItem>()).Add(item.ToUIBarButtonItem()); + } + + if (primaries != null) + primaries.Reverse(); + NavigationItem.SetRightBarButtonItems(primaries == null ? new UIBarButtonItem[0] : primaries.ToArray(), false); + ToolbarItems = secondaries == null ? new UIBarButtonItem[0] : secondaries.ToArray(); + + NavigationRenderer n; + if (_navigation.TryGetTarget(out n)) + n.UpdateToolBarVisible(); + } + } + } +}
\ No newline at end of file diff --git a/Xamarin.Forms.Platform.iOS/Renderers/OpenGLViewRenderer.cs b/Xamarin.Forms.Platform.iOS/Renderers/OpenGLViewRenderer.cs new file mode 100644 index 00000000..84c67744 --- /dev/null +++ b/Xamarin.Forms.Platform.iOS/Renderers/OpenGLViewRenderer.cs @@ -0,0 +1,122 @@ +using System; +using System.Drawing; +using System.ComponentModel; +#if __UNIFIED__ +using GLKit; +using OpenGLES; +using Foundation; +using CoreAnimation; +#else +using MonoTouch.GLKit; +using MonoTouch.OpenGLES; +using MonoTouch.Foundation; +using MonoTouch.CoreAnimation; +#endif +#if __UNIFIED__ +using RectangleF = CoreGraphics.CGRect; +using SizeF = CoreGraphics.CGSize; +using PointF = CoreGraphics.CGPoint; + +#else +using nfloat=System.Single; +using nint=System.Int32; +using nuint=System.UInt32; +#endif + +namespace Xamarin.Forms.Platform.iOS +{ + internal class OpenGLViewRenderer : ViewRenderer<OpenGLView, GLKView> + { + CADisplayLink _displayLink; + + public void Display(object sender, EventArgs eventArgs) + { + if (Element.HasRenderLoop) + return; + SetupRenderLoop(true); + } + + protected override void Dispose(bool disposing) + { + if (_displayLink != null) + { + _displayLink.Invalidate(); + _displayLink.Dispose(); + _displayLink = null; + + if (Element != null) + ((IOpenGlViewController)Element).DisplayRequested -= Display; + } + + base.Dispose(disposing); + } + + protected override void OnElementChanged(ElementChangedEventArgs<OpenGLView> e) + { + if (e.OldElement != null) + ((IOpenGlViewController)e.OldElement).DisplayRequested -= Display; + + if (e.NewElement != null) + { + var context = new EAGLContext(EAGLRenderingAPI.OpenGLES2); + var glkView = new GLKView(RectangleF.Empty) { Context = context, DrawableDepthFormat = GLKViewDrawableDepthFormat.Format24, Delegate = new Delegate(e.NewElement) }; + SetNativeControl(glkView); + + ((IOpenGlViewController)e.NewElement).DisplayRequested += Display; + + SetupRenderLoop(false); + } + + base.OnElementChanged(e); + } + + protected override void OnElementPropertyChanged(object sender, PropertyChangedEventArgs e) + { + base.OnElementPropertyChanged(sender, e); + + if (e.PropertyName == OpenGLView.HasRenderLoopProperty.PropertyName) + SetupRenderLoop(false); + } + + void SetupRenderLoop(bool oneShot) + { + if (_displayLink != null) + return; + if (!oneShot && !Element.HasRenderLoop) + return; + + _displayLink = CADisplayLink.Create(() => + { + var control = Control; + var model = Element; + if (control != null) + control.Display(); + if (control == null || model == null || !model.HasRenderLoop) + { + _displayLink.Invalidate(); + _displayLink.Dispose(); + _displayLink = null; + } + }); + _displayLink.AddToRunLoop(NSRunLoop.Current, NSRunLoop.NSDefaultRunLoopMode); + } + + class Delegate : GLKViewDelegate + { + readonly OpenGLView _model; + + public Delegate(OpenGLView model) + { + _model = model; + } + + public override void DrawInRect(GLKView view, RectangleF rect) + { + var onDisplay = _model.OnDisplay; + if (onDisplay == null) + return; + onDisplay(rect.ToRectangle()); + } + } + } +}
\ No newline at end of file diff --git a/Xamarin.Forms.Platform.iOS/Renderers/PageRenderer.cs b/Xamarin.Forms.Platform.iOS/Renderers/PageRenderer.cs new file mode 100644 index 00000000..ddc0d39e --- /dev/null +++ b/Xamarin.Forms.Platform.iOS/Renderers/PageRenderer.cs @@ -0,0 +1,218 @@ +using System; +using System.Collections.Generic; +using System.ComponentModel; +using System.Linq; +#if __UNIFIED__ +using UIKit; + +#else +using MonoTouch.UIKit; +#endif + +namespace Xamarin.Forms.Platform.iOS +{ + public class PageRenderer : UIViewController, IVisualElementRenderer + { + bool _appeared; + bool _disposed; + EventTracker _events; + VisualElementPackager _packager; + VisualElementTracker _tracker; + + public PageRenderer() + { + if (!Forms.IsiOS7OrNewer) + WantsFullScreenLayout = true; + } + + public VisualElement Element { get; private set; } + + public event EventHandler<VisualElementChangedEventArgs> ElementChanged; + + public SizeRequest GetDesiredSize(double widthConstraint, double heightConstraint) + { + return NativeView.GetSizeRequest(widthConstraint, heightConstraint); + } + + public UIView NativeView + { + get { return _disposed ? null : View; } + } + + public void SetElement(VisualElement element) + { + var oldElement = Element; + Element = element; + UpdateTitle(); + + OnElementChanged(new VisualElementChangedEventArgs(oldElement, element)); + + if (Element != null && !string.IsNullOrEmpty(Element.AutomationId)) + SetAutomationId(Element.AutomationId); + + if (element != null) + element.SendViewInitialized(NativeView); + } + + public void SetElementSize(Size size) + { + Element.Layout(new Rectangle(Element.X, Element.Y, size.Width, size.Height)); + } + + public UIViewController ViewController + { + get { return _disposed ? null : this; } + } + + public override void ViewDidAppear(bool animated) + { + base.ViewDidAppear(animated); + + if (_appeared || _disposed) + return; + + _appeared = true; + ((Page)Element).SendAppearing(); + } + + public override void ViewDidDisappear(bool animated) + { + base.ViewDidDisappear(animated); + + if (!_appeared || _disposed) + return; + + _appeared = false; + ((Page)Element).SendDisappearing(); + } + + public override void ViewDidLoad() + { + base.ViewDidLoad(); + + var uiTapGestureRecognizer = new UITapGestureRecognizer(a => View.EndEditing(true)); + + uiTapGestureRecognizer.ShouldRecognizeSimultaneously = (recognizer, gestureRecognizer) => true; + uiTapGestureRecognizer.ShouldReceiveTouch = OnShouldReceiveTouch; + uiTapGestureRecognizer.DelaysTouchesBegan = uiTapGestureRecognizer.DelaysTouchesEnded = false; + View.AddGestureRecognizer(uiTapGestureRecognizer); + + UpdateBackground(); + + _packager = new VisualElementPackager(this); + _packager.Load(); + + Element.PropertyChanged += OnHandlePropertyChanged; + _tracker = new VisualElementTracker(this); + + _events = new EventTracker(this); + _events.LoadEvents(View); + + Element.SendViewInitialized(View); + } + + public override void ViewWillDisappear(bool animated) + { + base.ViewWillDisappear(animated); + + if (View.Window != null) + View.Window.EndEditing(true); + } + + protected override void Dispose(bool disposing) + { + if (disposing && !_disposed) + { + Element.PropertyChanged -= OnHandlePropertyChanged; + Platform.SetRenderer(Element, null); + if (_appeared) + ((Page)Element).SendDisappearing(); + + _appeared = false; + + if (_events != null) + { + _events.Dispose(); + _events = null; + } + + if (_packager != null) + { + _packager.Dispose(); + _packager = null; + } + + if (_tracker != null) + { + _tracker.Dispose(); + _tracker = null; + } + + Element = null; + _disposed = true; + } + + base.Dispose(disposing); + } + + protected virtual void OnElementChanged(VisualElementChangedEventArgs e) + { + var changed = ElementChanged; + if (changed != null) + changed(this, e); + } + + protected virtual void SetAutomationId(string id) + { + if (NativeView != null) + NativeView.AccessibilityIdentifier = id; + } + + void OnHandlePropertyChanged(object sender, PropertyChangedEventArgs e) + { + if (e.PropertyName == VisualElement.BackgroundColorProperty.PropertyName) + UpdateBackground(); + else if (e.PropertyName == Page.BackgroundImageProperty.PropertyName) + UpdateBackground(); + else if (e.PropertyName == Page.TitleProperty.PropertyName) + UpdateTitle(); + } + + bool OnShouldReceiveTouch(UIGestureRecognizer recognizer, UITouch touch) + { + if (ViewAndSuperviewsOfView(touch.View).Any(v => v is UITableView || v is UITableViewCell || v.CanBecomeFirstResponder)) + return false; + return true; + } + + void UpdateBackground() + { + var bgImage = ((Page)Element).BackgroundImage; + if (!string.IsNullOrEmpty(bgImage)) + { + View.BackgroundColor = UIColor.FromPatternImage(UIImage.FromBundle(bgImage)); + return; + } + var bgColor = Element.BackgroundColor; + if (bgColor.IsDefault) + View.BackgroundColor = UIColor.White; + else + View.BackgroundColor = bgColor.ToUIColor(); + } + + void UpdateTitle() + { + if (!string.IsNullOrWhiteSpace(((Page)Element).Title)) + Title = ((Page)Element).Title; + } + + IEnumerable<UIView> ViewAndSuperviewsOfView(UIView view) + { + while (view != null) + { + yield return view; + view = view.Superview; + } + } + } +}
\ No newline at end of file diff --git a/Xamarin.Forms.Platform.iOS/Renderers/PhoneMasterDetailRenderer.cs b/Xamarin.Forms.Platform.iOS/Renderers/PhoneMasterDetailRenderer.cs new file mode 100644 index 00000000..070a0389 --- /dev/null +++ b/Xamarin.Forms.Platform.iOS/Renderers/PhoneMasterDetailRenderer.cs @@ -0,0 +1,401 @@ +using System; +using System.Linq; +using System.Drawing; +using System.ComponentModel; +#if __UNIFIED__ +using UIKit; +#else +using MonoTouch.UIKit; +#endif +#if __UNIFIED__ +using RectangleF = CoreGraphics.CGRect; +using SizeF = CoreGraphics.CGSize; +using PointF = CoreGraphics.CGPoint; + +#else +using nfloat=System.Single; +using nint=System.Int32; +using nuint=System.UInt32; +#endif + +namespace Xamarin.Forms.Platform.iOS +{ + public class PhoneMasterDetailRenderer : UIViewController, IVisualElementRenderer + { + UIView _clickOffView; + UIViewController _detailController; + + bool _disposed; + EventTracker _events; + + UIViewController _masterController; + + UIPanGestureRecognizer _panGesture; + + bool _presented; + UIGestureRecognizer _tapGesture; + + VisualElementTracker _tracker; + + public PhoneMasterDetailRenderer() + { + if (!Forms.IsiOS7OrNewer) + WantsFullScreenLayout = true; + } + + bool Presented + { + get { return _presented; } + set + { + if (_presented == value) + return; + _presented = value; + LayoutChildren(true); + if (value) + AddClickOffView(); + else + RemoveClickOffView(); + + ((IElementController)Element).SetValueFromRenderer(MasterDetailPage.IsPresentedProperty, value); + } + } + + public VisualElement Element { get; private set; } + + public event EventHandler<VisualElementChangedEventArgs> ElementChanged; + + public SizeRequest GetDesiredSize(double widthConstraint, double heightConstraint) + { + return NativeView.GetSizeRequest(widthConstraint, heightConstraint); + } + + public UIView NativeView + { + get { return View; } + } + + public void SetElement(VisualElement element) + { + var oldElement = Element; + Element = element; + Element.SizeChanged += PageOnSizeChanged; + + _masterController = new ChildViewController(); + _detailController = new ChildViewController(); + + _clickOffView = new UIView(); + _clickOffView.BackgroundColor = new Color(0, 0, 0, 0).ToUIColor(); + + Presented = ((MasterDetailPage)Element).IsPresented; + + OnElementChanged(new VisualElementChangedEventArgs(oldElement, element)); + + if (element != null) + element.SendViewInitialized(NativeView); + } + + public void SetElementSize(Size size) + { + Element.Layout(new Rectangle(Element.X, Element.Y, size.Width, size.Height)); + } + + public UIViewController ViewController + { + get { return this; } + } + + public override void ViewDidAppear(bool animated) + { + base.ViewDidAppear(animated); + ((Page)Element).SendAppearing(); + } + + public override void ViewDidDisappear(bool animated) + { + base.ViewDidDisappear(animated); + ((Page)Element).SendDisappearing(); + } + + public override void ViewDidLayoutSubviews() + { + base.ViewDidLayoutSubviews(); + + LayoutChildren(false); + } + + public override void ViewDidLoad() + { + base.ViewDidLoad(); + + _tracker = new VisualElementTracker(this); + _events = new EventTracker(this); + _events.LoadEvents(View); + + ((MasterDetailPage)Element).PropertyChanged += HandlePropertyChanged; + + _tapGesture = new UITapGestureRecognizer(() => + { + if (Presented) + Presented = false; + }); + _clickOffView.AddGestureRecognizer(_tapGesture); + + PackContainers(); + UpdateMasterDetailContainers(); + + UpdateBackground(); + + UpdatePanGesture(); + } + + public override void WillRotate(UIInterfaceOrientation toInterfaceOrientation, double duration) + { + if (!((MasterDetailPage)Element).ShouldShowSplitMode && _presented) + Presented = false; + + base.WillRotate(toInterfaceOrientation, duration); + } + + protected override void Dispose(bool disposing) + { + if (disposing && !_disposed) + { + Element.SizeChanged -= PageOnSizeChanged; + Element.PropertyChanged -= HandlePropertyChanged; + + if (_tracker != null) + { + _tracker.Dispose(); + _tracker = null; + } + + if (_events != null) + { + _events.Dispose(); + _events = null; + } + + if (_tapGesture != null) + { + if (_clickOffView != null && _clickOffView.GestureRecognizers.Contains(_panGesture)) + { + _clickOffView.GestureRecognizers.Remove(_tapGesture); + _clickOffView.Dispose(); + } + _tapGesture.Dispose(); + } + if (_panGesture != null) + { + if (View != null && View.GestureRecognizers.Contains(_panGesture)) + View.GestureRecognizers.Remove(_panGesture); + _panGesture.Dispose(); + } + + EmptyContainers(); + + ((Page)Element).SendDisappearing(); + + _disposed = true; + } + + base.Dispose(disposing); + } + + protected virtual void OnElementChanged(VisualElementChangedEventArgs e) + { + var changed = ElementChanged; + if (changed != null) + changed(this, e); + } + + void AddClickOffView() + { + View.Add(_clickOffView); + _clickOffView.Frame = _detailController.View.Frame; + } + + void EmptyContainers() + { + foreach (var child in _detailController.View.Subviews.Concat(_masterController.View.Subviews)) + child.RemoveFromSuperview(); + + foreach (var vc in _detailController.ChildViewControllers.Concat(_masterController.ChildViewControllers)) + vc.RemoveFromParentViewController(); + } + + void HandleMasterPropertyChanged(object sender, PropertyChangedEventArgs e) + { + if (e.PropertyName == Page.IconProperty.PropertyName || e.PropertyName == Page.TitleProperty.PropertyName) + MessagingCenter.Send<IVisualElementRenderer>(this, NavigationRenderer.UpdateToolbarButtons); + } + + void HandlePropertyChanged(object sender, PropertyChangedEventArgs e) + { + if (e.PropertyName == "Master" || e.PropertyName == "Detail") + UpdateMasterDetailContainers(); + else if (e.PropertyName == MasterDetailPage.IsPresentedProperty.PropertyName) + Presented = ((MasterDetailPage)Element).IsPresented; + else if (e.PropertyName == MasterDetailPage.IsGestureEnabledProperty.PropertyName) + UpdatePanGesture(); + else if (e.PropertyName == VisualElement.BackgroundColorProperty.PropertyName) + UpdateBackground(); + else if (e.PropertyName == Page.BackgroundImageProperty.PropertyName) + UpdateBackground(); + } + + void LayoutChildren(bool animated) + { + var frame = Element.Bounds.ToRectangleF(); + var masterFrame = frame; + masterFrame.Width = (int)(Math.Min(masterFrame.Width, masterFrame.Height) * 0.8); + + _masterController.View.Frame = masterFrame; + + var target = frame; + if (Presented) + target.X += masterFrame.Width; + + if (animated) + { + UIView.BeginAnimations("Flyout"); + var view = _detailController.View; + view.Frame = target; + UIView.SetAnimationCurve(UIViewAnimationCurve.EaseOut); + UIView.SetAnimationDuration(250); + UIView.CommitAnimations(); + } + else + _detailController.View.Frame = target; + + ((MasterDetailPage)Element).MasterBounds = new Rectangle(0, 0, masterFrame.Width, masterFrame.Height); + ((MasterDetailPage)Element).DetailBounds = new Rectangle(0, 0, frame.Width, frame.Height); + + if (Presented) + _clickOffView.Frame = _detailController.View.Frame; + } + + void PackContainers() + { + _detailController.View.BackgroundColor = new UIColor(1, 1, 1, 1); + View.AddSubview(_masterController.View); + View.AddSubview(_detailController.View); + + AddChildViewController(_masterController); + AddChildViewController(_detailController); + } + + void PageOnSizeChanged(object sender, EventArgs eventArgs) + { + LayoutChildren(false); + } + + void RemoveClickOffView() + { + _clickOffView.RemoveFromSuperview(); + } + + void UpdateBackground() + { + if (!string.IsNullOrEmpty(((Page)Element).BackgroundImage)) + View.BackgroundColor = UIColor.FromPatternImage(UIImage.FromBundle(((Page)Element).BackgroundImage)); + else if (Element.BackgroundColor == Color.Default) + View.BackgroundColor = UIColor.White; + else + View.BackgroundColor = Element.BackgroundColor.ToUIColor(); + } + + void UpdateMasterDetailContainers() + { + ((MasterDetailPage)Element).Master.PropertyChanged -= HandleMasterPropertyChanged; + + EmptyContainers(); + + if (Platform.GetRenderer(((MasterDetailPage)Element).Master) == null) + Platform.SetRenderer(((MasterDetailPage)Element).Master, Platform.CreateRenderer(((MasterDetailPage)Element).Master)); + if (Platform.GetRenderer(((MasterDetailPage)Element).Detail) == null) + Platform.SetRenderer(((MasterDetailPage)Element).Detail, Platform.CreateRenderer(((MasterDetailPage)Element).Detail)); + + var masterRenderer = Platform.GetRenderer(((MasterDetailPage)Element).Master); + var detailRenderer = Platform.GetRenderer(((MasterDetailPage)Element).Detail); + + ((MasterDetailPage)Element).Master.PropertyChanged += HandleMasterPropertyChanged; + + _masterController.View.AddSubview(masterRenderer.NativeView); + _masterController.AddChildViewController(masterRenderer.ViewController); + + _detailController.View.AddSubview(detailRenderer.NativeView); + _detailController.AddChildViewController(detailRenderer.ViewController); + } + + void UpdatePanGesture() + { + var model = (MasterDetailPage)Element; + if (!model.IsGestureEnabled) + { + if (_panGesture != null) + View.RemoveGestureRecognizer(_panGesture); + return; + } + + if (_panGesture != null) + { + View.AddGestureRecognizer(_panGesture); + return; + } + + UITouchEventArgs shouldRecieve = (g, t) => !(t.View is UISlider); + var center = new PointF(); + _panGesture = new UIPanGestureRecognizer(g => + { + switch (g.State) + { + case UIGestureRecognizerState.Began: + center = g.LocationInView(g.View); + break; + case UIGestureRecognizerState.Changed: + var currentPosition = g.LocationInView(g.View); + var motion = currentPosition.X - center.X; + var detailView = _detailController.View; + var targetFrame = detailView.Frame; + if (Presented) + targetFrame.X = (nfloat)Math.Max(0, _masterController.View.Frame.Width + Math.Min(0, motion)); + else + targetFrame.X = (nfloat)Math.Min(_masterController.View.Frame.Width, Math.Max(0, motion)); + detailView.Frame = targetFrame; + break; + case UIGestureRecognizerState.Ended: + var detailFrame = _detailController.View.Frame; + var masterFrame = _masterController.View.Frame; + if (Presented) + { + if (detailFrame.X < masterFrame.Width * .75) + Presented = false; + else + LayoutChildren(true); + } + else + { + if (detailFrame.X > masterFrame.Width * .25) + Presented = true; + else + LayoutChildren(true); + } + break; + } + }); + _panGesture.ShouldReceiveTouch = shouldRecieve; + _panGesture.MaximumNumberOfTouches = 2; + View.AddGestureRecognizer(_panGesture); + } + + class ChildViewController : UIViewController + { + public override void ViewDidLayoutSubviews() + { + foreach (var vc in ChildViewControllers) + vc.View.Frame = View.Bounds; + } + } + } +}
\ No newline at end of file diff --git a/Xamarin.Forms.Platform.iOS/Renderers/PickerRenderer.cs b/Xamarin.Forms.Platform.iOS/Renderers/PickerRenderer.cs new file mode 100644 index 00000000..e47d2fa4 --- /dev/null +++ b/Xamarin.Forms.Platform.iOS/Renderers/PickerRenderer.cs @@ -0,0 +1,180 @@ +using System; +using System.ComponentModel; +using System.Drawing; +#if __UNIFIED__ +using UIKit; +#else +using MonoTouch.UIKit; +#endif +#if __UNIFIED__ +using RectangleF = CoreGraphics.CGRect; +using SizeF = CoreGraphics.CGSize; +using PointF = CoreGraphics.CGPoint; + +#else +using nfloat=System.Single; +using nint=System.Int32; +using nuint=System.UInt32; +#endif + +namespace Xamarin.Forms.Platform.iOS +{ + public class PickerRenderer : ViewRenderer<Picker, UITextField> + { + UIPickerView _picker; + + protected override void OnElementChanged(ElementChangedEventArgs<Picker> e) + { + if (e.OldElement != null) + ((ObservableList<string>)e.OldElement.Items).CollectionChanged -= RowsCollectionChanged; + + if (e.NewElement != null) + { + if (Control == null) + { + var entry = new NoCaretField { BorderStyle = UITextBorderStyle.RoundedRect }; + + entry.Started += OnStarted; + entry.Ended += OnEnded; + + _picker = new UIPickerView(); + + var width = UIScreen.MainScreen.Bounds.Width; + var toolbar = new UIToolbar(new RectangleF(0, 0, width, 44)) { BarStyle = UIBarStyle.Default, Translucent = true }; + var spacer = new UIBarButtonItem(UIBarButtonSystemItem.FlexibleSpace); + var doneButton = new UIBarButtonItem(UIBarButtonSystemItem.Done, (o, a) => + { + var s = (PickerSource)_picker.Model; + if (s.SelectedIndex == -1 && Element.Items != null && Element.Items.Count > 0) + UpdatePickerSelectedIndex(0); + UpdatePickerFromModel(s); + entry.ResignFirstResponder(); + }); + + toolbar.SetItems(new[] { spacer, doneButton }, false); + + entry.InputView = _picker; + entry.InputAccessoryView = toolbar; + + SetNativeControl(entry); + } + + _picker.Model = new PickerSource(this); + + UpdatePicker(); + + ((ObservableList<string>)e.NewElement.Items).CollectionChanged += RowsCollectionChanged; + } + + base.OnElementChanged(e); + } + + protected override void OnElementPropertyChanged(object sender, PropertyChangedEventArgs e) + { + base.OnElementPropertyChanged(sender, e); + if (e.PropertyName == Picker.TitleProperty.PropertyName) + UpdatePicker(); + if (e.PropertyName == Picker.SelectedIndexProperty.PropertyName) + UpdatePicker(); + } + + void OnEnded(object sender, EventArgs eventArgs) + { + ((IElementController)Element).SetValueFromRenderer(VisualElement.IsFocusedPropertyKey, false); + } + + void OnStarted(object sender, EventArgs eventArgs) + { + ((IElementController)Element).SetValueFromRenderer(VisualElement.IsFocusedPropertyKey, true); + } + + void RowsCollectionChanged(object sender, EventArgs e) + { + UpdatePicker(); + } + + void UpdatePicker() + { + var selectedIndex = Element.SelectedIndex; + var items = Element.Items; + Control.Placeholder = Element.Title; + var oldText = Control.Text; + Control.Text = selectedIndex == -1 || items == null ? "" : items[selectedIndex]; + UpdatePickerNativeSize(oldText); + _picker.ReloadAllComponents(); + if (items == null || items.Count == 0) + return; + + UpdatePickerSelectedIndex(selectedIndex); + } + + void UpdatePickerFromModel(PickerSource s) + { + if (Element != null) + { + var oldText = Control.Text; + ((IElementController)Element).SetValueFromRenderer(Picker.SelectedIndexProperty, s.SelectedIndex); + Control.Text = s.SelectedItem; + UpdatePickerNativeSize(oldText); + } + } + + void UpdatePickerNativeSize(string oldText) + { + if (oldText != Control.Text) + ((IVisualElementController)Element).NativeSizeChanged(); + } + + void UpdatePickerSelectedIndex(int formsIndex) + { + var source = (PickerSource)_picker.Model; + source.SelectedIndex = formsIndex; + source.SelectedItem = formsIndex >= 0 ? Element.Items[formsIndex] : null; + _picker.Select(Math.Max(formsIndex, 0), 0, true); + } + + class PickerSource : UIPickerViewModel + { + readonly PickerRenderer _renderer; + + public PickerSource(PickerRenderer model) + { + _renderer = model; + } + + public int SelectedIndex { get; internal set; } + + public string SelectedItem { get; internal set; } + + public override nint GetComponentCount(UIPickerView picker) + { + return 1; + } + + public override nint GetRowsInComponent(UIPickerView pickerView, nint component) + { + return _renderer.Element.Items != null ? _renderer.Element.Items.Count : 0; + } + + public override string GetTitle(UIPickerView picker, nint row, nint component) + { + return _renderer.Element.Items[(int)row]; + } + + public override void Selected(UIPickerView picker, nint row, nint component) + { + if (_renderer.Element.Items.Count == 0) + { + SelectedItem = null; + SelectedIndex = -1; + } + else + { + SelectedItem = _renderer.Element.Items[(int)row]; + SelectedIndex = (int)row; + } + _renderer.UpdatePickerFromModel(this); + } + } + } +}
\ No newline at end of file diff --git a/Xamarin.Forms.Platform.iOS/Renderers/ProgressBarRenderer.cs b/Xamarin.Forms.Platform.iOS/Renderers/ProgressBarRenderer.cs new file mode 100644 index 00000000..37091720 --- /dev/null +++ b/Xamarin.Forms.Platform.iOS/Renderers/ProgressBarRenderer.cs @@ -0,0 +1,67 @@ +using System.Drawing; +using System.ComponentModel; +#if __UNIFIED__ +using UIKit; +#else +using MonoTouch.UIKit; +#endif +#if __UNIFIED__ +using RectangleF = CoreGraphics.CGRect; +using SizeF = CoreGraphics.CGSize; +using PointF = CoreGraphics.CGPoint; + +#else +using nfloat=System.Single; +using nint=System.Int32; +using nuint=System.UInt32; +#endif + +namespace Xamarin.Forms.Platform.iOS +{ + public class ProgressBarRenderer : ViewRenderer<ProgressBar, UIProgressView> + { + public override SizeF SizeThatFits(SizeF size) + { + // progress bar will size itself to be as wide as the request, even if its inifinite + // we want the minimum need size + var result = base.SizeThatFits(size); + return new SizeF(10, result.Height); + } + + protected override void OnElementChanged(ElementChangedEventArgs<ProgressBar> e) + { + if (e.NewElement != null) + { + if (Control == null) + SetNativeControl(new UIProgressView(UIProgressViewStyle.Default)); + + UpdateProgress(); + } + + base.OnElementChanged(e); + } + + protected override void OnElementPropertyChanged(object sender, PropertyChangedEventArgs e) + { + base.OnElementPropertyChanged(sender, e); + + if (e.PropertyName == ProgressBar.ProgressProperty.PropertyName) + UpdateProgress(); + } + + protected override void SetBackgroundColor(Color color) + { + base.SetBackgroundColor(color); + + if (Control == null) + return; + + Control.TrackTintColor = color != Color.Default ? color.ToUIColor() : null; + } + + void UpdateProgress() + { + Control.Progress = (float)Element.Progress; + } + } +}
\ No newline at end of file diff --git a/Xamarin.Forms.Platform.iOS/Renderers/ScrollViewRenderer.cs b/Xamarin.Forms.Platform.iOS/Renderers/ScrollViewRenderer.cs new file mode 100644 index 00000000..1e8ea1fc --- /dev/null +++ b/Xamarin.Forms.Platform.iOS/Renderers/ScrollViewRenderer.cs @@ -0,0 +1,246 @@ +using System; +using System.ComponentModel; +using System.Drawing; +#if __UNIFIED__ +using UIKit; +using Foundation; +using ObjCRuntime; +using PointF = CoreGraphics.CGPoint; +using RectangleF = CoreGraphics.CGRect; + +#else +using MonoTouch.Foundation; +using MonoTouch.ObjCRuntime; +using MonoTouch.UIKit; +using nfloat=System.Single; + +#endif + +namespace Xamarin.Forms.Platform.iOS +{ + public class ScrollViewRenderer : UIScrollView, IVisualElementRenderer + { + EventTracker _events; + KeyboardInsetTracker _insetTracker; + + VisualElementPackager _packager; + + RectangleF _previousFrame; + ScrollToRequestedEventArgs _requestedScroll; + VisualElementTracker _tracker; + + public ScrollViewRenderer() : base(RectangleF.Empty) + { + ScrollAnimationEnded += HandleScrollAnimationEnded; + Scrolled += HandleScrolled; + } + + protected IScrollViewController Controller + { + get { return (IScrollViewController)Element; } + } + + ScrollView ScrollView + { + get { return Element as ScrollView; } + } + + public VisualElement Element { get; private set; } + + public event EventHandler<VisualElementChangedEventArgs> ElementChanged; + + public SizeRequest GetDesiredSize(double widthConstraint, double heightConstraint) + { + return NativeView.GetSizeRequest(widthConstraint, heightConstraint, 44, 44); + } + + public UIView NativeView + { + get { return this; } + } + + public void SetElement(VisualElement element) + { + _requestedScroll = null; + var oldElement = Element; + Element = element; + + if (oldElement != null) + { + oldElement.PropertyChanged -= HandlePropertyChanged; + ((IScrollViewController)oldElement).ScrollToRequested -= OnScrollToRequested; + } + + if (element != null) + { + element.PropertyChanged += HandlePropertyChanged; + ((IScrollViewController)element).ScrollToRequested += OnScrollToRequested; + if (_packager == null) + { + DelaysContentTouches = true; + + _packager = new VisualElementPackager(this); + _packager.Load(); + + _tracker = new VisualElementTracker(this); + _tracker.NativeControlUpdated += OnNativeControlUpdated; + _events = new EventTracker(this); + _events.LoadEvents(this); + + _insetTracker = new KeyboardInsetTracker(this, () => Window, insets => ContentInset = ScrollIndicatorInsets = insets, point => + { + var offset = ContentOffset; + offset.Y += point.Y; + SetContentOffset(offset, true); + }); + } + + UpdateContentSize(); + UpdateBackgroundColor(); + + OnElementChanged(new VisualElementChangedEventArgs(oldElement, element)); + + if (element != null) + element.SendViewInitialized(this); + + if (!string.IsNullOrEmpty(element.AutomationId)) + AccessibilityIdentifier = element.AutomationId; + } + } + + public void SetElementSize(Size size) + { + Layout.LayoutChildIntoBoundingRegion(Element, new Rectangle(Element.X, Element.Y, size.Width, size.Height)); + } + + public UIViewController ViewController + { + get { return null; } + } + + public override void LayoutSubviews() + { + base.LayoutSubviews(); + + if (_requestedScroll != null && Superview != null) + { + var request = _requestedScroll; + _requestedScroll = null; + OnScrollToRequested(this, request); + } + + if (_previousFrame != Frame) + { + _previousFrame = Frame; + _insetTracker?.UpdateInsets(); + } + } + + protected override void Dispose(bool disposing) + { + if (disposing) + { + if (_packager == null) + return; + + SetElement(null); + + _packager.Dispose(); + _packager = null; + + _tracker.NativeControlUpdated -= OnNativeControlUpdated; + _tracker.Dispose(); + _tracker = null; + + _events.Dispose(); + _events = null; + + _insetTracker.Dispose(); + _insetTracker = null; + + ScrollAnimationEnded -= HandleScrollAnimationEnded; + Scrolled -= HandleScrolled; + } + + base.Dispose(disposing); + } + + protected virtual void OnElementChanged(VisualElementChangedEventArgs e) + { + var changed = ElementChanged; + if (changed != null) + changed(this, e); + } + + void HandlePropertyChanged(object sender, PropertyChangedEventArgs e) + { + if (e.PropertyName == ScrollView.ContentSizeProperty.PropertyName) + UpdateContentSize(); + else if (e.PropertyName == VisualElement.BackgroundColorProperty.PropertyName) + UpdateBackgroundColor(); + } + + void HandleScrollAnimationEnded(object sender, EventArgs e) + { + Controller.SendScrollFinished(); + } + + void HandleScrolled(object sender, EventArgs e) + { + UpdateScrollPosition(); + } + + void OnNativeControlUpdated(object sender, EventArgs eventArgs) + { + ContentSize = Bounds.Size; + UpdateContentSize(); + } + + void OnScrollToRequested(object sender, ScrollToRequestedEventArgs e) + { + if (Superview == null) + { + _requestedScroll = e; + return; + } + if (e.Mode == ScrollToMode.Position) + SetContentOffset(new PointF((nfloat)e.ScrollX, (nfloat)e.ScrollY), e.ShouldAnimate); + else + { + var positionOnScroll = Controller.GetScrollPositionForElement(e.Element as VisualElement, e.Position); + switch (ScrollView.Orientation) + { + case ScrollOrientation.Horizontal: + SetContentOffset(new PointF((nfloat)positionOnScroll.X, ContentOffset.Y), e.ShouldAnimate); + break; + case ScrollOrientation.Vertical: + SetContentOffset(new PointF(ContentOffset.X, (nfloat)positionOnScroll.Y), e.ShouldAnimate); + break; + case ScrollOrientation.Both: + SetContentOffset(new PointF((nfloat)positionOnScroll.X, (nfloat)positionOnScroll.Y), e.ShouldAnimate); + break; + } + } + if (!e.ShouldAnimate) + Controller.SendScrollFinished(); + } + + void UpdateBackgroundColor() + { + BackgroundColor = Element.BackgroundColor.ToUIColor(Color.Transparent); + } + + void UpdateContentSize() + { + var contentSize = ((ScrollView)Element).ContentSize.ToSizeF(); + if (!contentSize.IsEmpty) + ContentSize = contentSize; + } + + void UpdateScrollPosition() + { + if (ScrollView != null) + Controller.SetScrolledPosition(ContentOffset.X, ContentOffset.Y); + } + } +}
\ No newline at end of file diff --git a/Xamarin.Forms.Platform.iOS/Renderers/SearchBarRenderer.cs b/Xamarin.Forms.Platform.iOS/Renderers/SearchBarRenderer.cs new file mode 100644 index 00000000..e5e156c4 --- /dev/null +++ b/Xamarin.Forms.Platform.iOS/Renderers/SearchBarRenderer.cs @@ -0,0 +1,249 @@ +using System; +using System.Drawing; +using System.ComponentModel; +#if __UNIFIED__ +using UIKit; + +#else +using MonoTouch.UIKit; +#endif + +namespace Xamarin.Forms.Platform.iOS +{ + public class SearchBarRenderer : ViewRenderer<SearchBar, UISearchBar> + { + UIColor _cancelButtonTextColorDefaultDisabled; + UIColor _cancelButtonTextColorDefaultHighlighted; + UIColor _cancelButtonTextColorDefaultNormal; + + UIColor _defaultTextColor; + UIColor _defaultTintColor; + UITextField _textField; + + protected override void Dispose(bool disposing) + { + if (disposing) + { + if (Control != null) + { + Control.CancelButtonClicked -= OnCancelClicked; + Control.SearchButtonClicked -= OnSearchButtonClicked; + Control.TextChanged -= OnTextChanged; + + Control.OnEditingStarted -= OnEditingEnded; + Control.OnEditingStopped -= OnEditingStarted; + } + } + + base.Dispose(disposing); + } + + protected override void OnElementChanged(ElementChangedEventArgs<SearchBar> e) + { + if (e.NewElement != null) + { + if (Control == null) + { + var searchBar = new UISearchBar(RectangleF.Empty) { ShowsCancelButton = true, BarStyle = UIBarStyle.Default }; + + var cancelButton = searchBar.FindDescendantView<UIButton>(); + _cancelButtonTextColorDefaultNormal = cancelButton.TitleColor(UIControlState.Normal); + _cancelButtonTextColorDefaultHighlighted = cancelButton.TitleColor(UIControlState.Highlighted); + _cancelButtonTextColorDefaultDisabled = cancelButton.TitleColor(UIControlState.Disabled); + + SetNativeControl(searchBar); + + Control.CancelButtonClicked += OnCancelClicked; + Control.SearchButtonClicked += OnSearchButtonClicked; + Control.TextChanged += OnTextChanged; + + Control.OnEditingStarted += OnEditingStarted; + Control.OnEditingStopped += OnEditingEnded; + } + + UpdatePlaceholder(); + UpdateText(); + UpdateFont(); + UpdateIsEnabled(); + UpdateCancelButton(); + UpdateAlignment(); + UpdateTextColor(); + } + + base.OnElementChanged(e); + } + + protected override void OnElementPropertyChanged(object sender, PropertyChangedEventArgs e) + { + base.OnElementPropertyChanged(sender, e); + + if (e.PropertyName == SearchBar.PlaceholderProperty.PropertyName || e.PropertyName == SearchBar.PlaceholderColorProperty.PropertyName) + UpdatePlaceholder(); + else if (e.PropertyName == VisualElement.IsEnabledProperty.PropertyName) + { + UpdateIsEnabled(); + UpdateTextColor(); + UpdatePlaceholder(); + } + else if (e.PropertyName == SearchBar.TextColorProperty.PropertyName) + UpdateTextColor(); + else if (e.PropertyName == SearchBar.TextProperty.PropertyName) + UpdateText(); + else if (e.PropertyName == SearchBar.CancelButtonColorProperty.PropertyName) + UpdateCancelButton(); + else if (e.PropertyName == SearchBar.FontAttributesProperty.PropertyName) + UpdateFont(); + else if (e.PropertyName == SearchBar.FontFamilyProperty.PropertyName) + UpdateFont(); + else if (e.PropertyName == SearchBar.FontSizeProperty.PropertyName) + UpdateFont(); + else if (e.PropertyName == SearchBar.HorizontalTextAlignmentProperty.PropertyName) + UpdateAlignment(); + } + + protected override void SetBackgroundColor(Color color) + { + base.SetBackgroundColor(color); + + if (Control == null) + return; + + if (_defaultTintColor == null) + { + if (Forms.IsiOS7OrNewer) + _defaultTintColor = Control.BarTintColor; + else + _defaultTintColor = Control.TintColor; + } + + if (Forms.IsiOS7OrNewer) + Control.BarTintColor = color.ToUIColor(_defaultTintColor); + else + Control.TintColor = color.ToUIColor(_defaultTintColor); + + if (color.A < 1) + Control.SetBackgroundImage(new UIImage(), UIBarPosition.Any, UIBarMetrics.Default); + + // updating BarTintColor resets the button color so we need to update the button color again + UpdateCancelButton(); + } + + void OnCancelClicked(object sender, EventArgs args) + { + ((IElementController)Element).SetValueFromRenderer(SearchBar.TextProperty, null); + Control.ResignFirstResponder(); + } + + void OnEditingEnded(object sender, EventArgs e) + { + if (Element != null) + ((IElementController)Element).SetValueFromRenderer(VisualElement.IsFocusedPropertyKey, false); + } + + void OnEditingStarted(object sender, EventArgs e) + { + if (Element != null) + ((IElementController)Element).SetValueFromRenderer(VisualElement.IsFocusedPropertyKey, true); + } + + void OnSearchButtonClicked(object sender, EventArgs e) + { + Element.OnSearchButtonPressed(); + Control.ResignFirstResponder(); + } + + void OnTextChanged(object sender, UISearchBarTextChangedEventArgs a) + { + ((IElementController)Element).SetValueFromRenderer(SearchBar.TextProperty, Control.Text); + } + + void UpdateAlignment() + { + _textField = _textField ?? Control.FindDescendantView<UITextField>(); + + if (_textField == null) + return; + + _textField.TextAlignment = Element.HorizontalTextAlignment.ToNativeTextAlignment(); + } + + void UpdateCancelButton() + { + Control.ShowsCancelButton = !string.IsNullOrEmpty(Control.Text); + + // We can't cache the cancel button reference because iOS drops it when it's not displayed + // and creates a brand new one when necessary, so we have to look for it each time + var cancelButton = Control.FindDescendantView<UIButton>(); + + if (cancelButton == null) + return; + + if (Element.CancelButtonColor == Color.Default) + { + cancelButton.SetTitleColor(_cancelButtonTextColorDefaultNormal, UIControlState.Normal); + cancelButton.SetTitleColor(_cancelButtonTextColorDefaultHighlighted, UIControlState.Highlighted); + cancelButton.SetTitleColor(_cancelButtonTextColorDefaultDisabled, UIControlState.Disabled); + } + else + { + cancelButton.SetTitleColor(Element.CancelButtonColor.ToUIColor(), UIControlState.Normal); + cancelButton.SetTitleColor(Element.CancelButtonColor.ToUIColor(), UIControlState.Highlighted); + cancelButton.SetTitleColor(_cancelButtonTextColorDefaultDisabled, UIControlState.Disabled); + } + } + + void UpdateFont() + { + _textField = _textField ?? Control.FindDescendantView<UITextField>(); + + if (_textField == null) + return; + + _textField.Font = Element.ToUIFont(); + } + + void UpdateIsEnabled() + { + Control.UserInteractionEnabled = Element.IsEnabled; + } + + void UpdatePlaceholder() + { + _textField = _textField ?? Control.FindDescendantView<UITextField>(); + + if (_textField == null) + return; + + var formatted = (FormattedString)Element.Placeholder ?? string.Empty; + var targetColor = Element.PlaceholderColor; + + // Placeholder default color is 70% gray + // https://developer.apple.com/library/prerelease/ios/documentation/UIKit/Reference/UITextField_Class/index.html#//apple_ref/occ/instp/UITextField/placeholder + + var color = Element.IsEnabled && !targetColor.IsDefault ? targetColor : ColorExtensions.SeventyPercentGrey.ToColor(); + + _textField.AttributedPlaceholder = formatted.ToAttributed(Element, color); + } + + void UpdateText() + { + Control.Text = Element.Text; + UpdateCancelButton(); + } + + void UpdateTextColor() + { + _textField = _textField ?? Control.FindDescendantView<UITextField>(); + + if (_textField == null) + return; + + _defaultTextColor = _defaultTextColor ?? _textField.TextColor; + var targetColor = Element.TextColor; + + var color = Element.IsEnabled && !targetColor.IsDefault ? targetColor : _defaultTextColor.ToColor(); + + _textField.TextColor = color.ToUIColor(); + } + } +}
\ No newline at end of file diff --git a/Xamarin.Forms.Platform.iOS/Renderers/SliderRenderer.cs b/Xamarin.Forms.Platform.iOS/Renderers/SliderRenderer.cs new file mode 100644 index 00000000..2df61f2f --- /dev/null +++ b/Xamarin.Forms.Platform.iOS/Renderers/SliderRenderer.cs @@ -0,0 +1,99 @@ +using System; +using System.Drawing; +using System.ComponentModel; +#if __UNIFIED__ +using UIKit; +#else +using MonoTouch.UIKit; +#endif +#if __UNIFIED__ +using RectangleF = CoreGraphics.CGRect; +using SizeF = CoreGraphics.CGSize; +using PointF = CoreGraphics.CGPoint; + +#else +using nfloat=System.Single; +using nint=System.Int32; +using nuint=System.UInt32; +#endif + +namespace Xamarin.Forms.Platform.iOS +{ + public class SliderRenderer : ViewRenderer<Slider, UISlider> + { + SizeF _fitSize; + + public override SizeF SizeThatFits(SizeF size) + { + return _fitSize; + } + + protected override void Dispose(bool disposing) + { + if (Control != null) + Control.ValueChanged -= OnControlValueChanged; + + base.Dispose(disposing); + } + + protected override void OnElementChanged(ElementChangedEventArgs<Slider> e) + { + if (e.NewElement != null) + { + if (Control == null) + { + SetNativeControl(new UISlider { Continuous = true }); + Control.ValueChanged += OnControlValueChanged; + + // sliders SizeThatFits method returns non-useful answers + // this however gives a very useful answer + Control.SizeToFit(); + _fitSize = Control.Bounds.Size; + + // except if your not running iOS 7... then it fails... + if (_fitSize.Width <= 0 || _fitSize.Height <= 0) + _fitSize = new SizeF(22, 22); // Per the glorious documentation known as the SDK docs + } + + UpdateMaximum(); + UpdateMinimum(); + UpdateValue(); + } + + base.OnElementChanged(e); + } + + protected override void OnElementPropertyChanged(object sender, PropertyChangedEventArgs e) + { + base.OnElementPropertyChanged(sender, e); + + if (e.PropertyName == Slider.MaximumProperty.PropertyName) + UpdateMaximum(); + else if (e.PropertyName == Slider.MinimumProperty.PropertyName) + UpdateMinimum(); + else if (e.PropertyName == Slider.ValueProperty.PropertyName) + UpdateValue(); + } + + void OnControlValueChanged(object sender, EventArgs eventArgs) + { + ((IElementController)Element).SetValueFromRenderer(Slider.ValueProperty, Control.Value); + } + + void UpdateMaximum() + { + Control.MaxValue = (float)Element.Maximum; + } + + void UpdateMinimum() + { + Control.MinValue = (float)Element.Minimum; + } + + void UpdateValue() + { + if ((float)Element.Value != Control.Value) + Control.Value = (float)Element.Value; + } + } +}
\ No newline at end of file diff --git a/Xamarin.Forms.Platform.iOS/Renderers/StepperRenderer.cs b/Xamarin.Forms.Platform.iOS/Renderers/StepperRenderer.cs new file mode 100644 index 00000000..0cb0fcb7 --- /dev/null +++ b/Xamarin.Forms.Platform.iOS/Renderers/StepperRenderer.cs @@ -0,0 +1,82 @@ +using System; +using System.Drawing; +using System.ComponentModel; +#if __UNIFIED__ +using UIKit; + +#else +using MonoTouch.UIKit; +#endif + +namespace Xamarin.Forms.Platform.iOS +{ + public class StepperRenderer : ViewRenderer<Stepper, UIStepper> + { + protected override void Dispose(bool disposing) + { + if (Control != null) + Control.ValueChanged -= OnValueChanged; + + base.Dispose(disposing); + } + + protected override void OnElementChanged(ElementChangedEventArgs<Stepper> e) + { + if (e.NewElement != null) + { + if (Control == null) + { + SetNativeControl(new UIStepper(RectangleF.Empty)); + Control.ValueChanged += OnValueChanged; + } + + UpdateMinimum(); + UpdateMaximum(); + UpdateValue(); + UpdateIncrement(); + } + + base.OnElementChanged(e); + } + + protected override void OnElementPropertyChanged(object sender, PropertyChangedEventArgs e) + { + base.OnElementPropertyChanged(sender, e); + + if (e.PropertyName == Stepper.MinimumProperty.PropertyName) + UpdateMinimum(); + else if (e.PropertyName == Stepper.MaximumProperty.PropertyName) + UpdateMaximum(); + else if (e.PropertyName == Stepper.ValueProperty.PropertyName) + UpdateValue(); + else if (e.PropertyName == Stepper.IncrementProperty.PropertyName) + UpdateIncrement(); + } + + void OnValueChanged(object sender, EventArgs e) + { + ((IElementController)Element).SetValueFromRenderer(Stepper.ValueProperty, Control.Value); + } + + void UpdateIncrement() + { + Control.StepValue = Element.Increment; + } + + void UpdateMaximum() + { + Control.MaximumValue = Element.Maximum; + } + + void UpdateMinimum() + { + Control.MinimumValue = Element.Minimum; + } + + void UpdateValue() + { + if (Control.Value != Element.Value) + Control.Value = Element.Value; + } + } +}
\ No newline at end of file diff --git a/Xamarin.Forms.Platform.iOS/Renderers/SwitchRenderer.cs b/Xamarin.Forms.Platform.iOS/Renderers/SwitchRenderer.cs new file mode 100644 index 00000000..0bb7fbef --- /dev/null +++ b/Xamarin.Forms.Platform.iOS/Renderers/SwitchRenderer.cs @@ -0,0 +1,52 @@ +using System; +using System.Drawing; +#if __UNIFIED__ +using UIKit; + +#else +using MonoTouch.UIKit; +#endif + +namespace Xamarin.Forms.Platform.iOS +{ + public class SwitchRenderer : ViewRenderer<Switch, UISwitch> + { + protected override void Dispose(bool disposing) + { + if (disposing) + Control.ValueChanged -= OnControlValueChanged; + + base.Dispose(disposing); + } + + protected override void OnElementChanged(ElementChangedEventArgs<Switch> e) + { + if (e.OldElement != null) + e.OldElement.Toggled -= OnElementToggled; + + if (e.NewElement != null) + { + if (Control == null) + { + SetNativeControl(new UISwitch(RectangleF.Empty)); + Control.ValueChanged += OnControlValueChanged; + } + + Control.On = Element.IsToggled; + e.NewElement.Toggled += OnElementToggled; + } + + base.OnElementChanged(e); + } + + void OnControlValueChanged(object sender, EventArgs e) + { + ((IElementController)Element).SetValueFromRenderer(Switch.IsToggledProperty, Control.On); + } + + void OnElementToggled(object sender, EventArgs e) + { + Control.SetState(Element.IsToggled, true); + } + } +}
\ No newline at end of file diff --git a/Xamarin.Forms.Platform.iOS/Renderers/TabbedRenderer.cs b/Xamarin.Forms.Platform.iOS/Renderers/TabbedRenderer.cs new file mode 100644 index 00000000..7e5121fd --- /dev/null +++ b/Xamarin.Forms.Platform.iOS/Renderers/TabbedRenderer.cs @@ -0,0 +1,311 @@ +using System; +using System.Collections.Specialized; +using System.ComponentModel; +using System.Linq; +using System.Collections.Generic; +#if __UNIFIED__ +using UIKit; +#else +using MonoTouch.UIKit; +#endif +#if __UNIFIED__ +using RectangleF = CoreGraphics.CGRect; +using SizeF = CoreGraphics.CGSize; +using PointF = CoreGraphics.CGPoint; + +#else +using nfloat=System.Single; +using nint=System.Int32; +using nuint=System.UInt32; +#endif + +namespace Xamarin.Forms.Platform.iOS +{ + public class TabbedRenderer : UITabBarController, IVisualElementRenderer + { + bool _loaded; + Size _queuedSize; + + public override UIViewController SelectedViewController + { + get { return base.SelectedViewController; } + set + { + base.SelectedViewController = value; + UpdateCurrentPage(); + } + } + + protected TabbedPage Tabbed + { + get { return (TabbedPage)Element; } + } + + public VisualElement Element { get; private set; } + + public event EventHandler<VisualElementChangedEventArgs> ElementChanged; + + public SizeRequest GetDesiredSize(double widthConstraint, double heightConstraint) + { + return NativeView.GetSizeRequest(widthConstraint, heightConstraint); + } + + public UIView NativeView + { + get { return View; } + } + + public void SetElement(VisualElement element) + { + var oldElement = Element; + Element = element; + + FinishedCustomizingViewControllers += HandleFinishedCustomizingViewControllers; + Tabbed.PropertyChanged += OnPropertyChanged; + Tabbed.PagesChanged += OnPagesChanged; + + OnElementChanged(new VisualElementChangedEventArgs(oldElement, element)); + + OnPagesChanged(null, new NotifyCollectionChangedEventArgs(NotifyCollectionChangedAction.Reset)); + + if (element != null) + element.SendViewInitialized(NativeView); + + //disable edit/reorder of tabs + CustomizableViewControllers = null; + } + + public void SetElementSize(Size size) + { + if (_loaded) + Element.Layout(new Rectangle(Element.X, Element.Y, size.Width, size.Height)); + else + _queuedSize = size; + } + + public UIViewController ViewController + { + get { return this; } + } + + public override void DidRotate(UIInterfaceOrientation fromInterfaceOrientation) + { + base.DidRotate(fromInterfaceOrientation); + + View.SetNeedsLayout(); + } + + public override void ViewDidAppear(bool animated) + { + ((TabbedPage)Element).SendAppearing(); + base.ViewDidAppear(animated); + } + + public override void ViewDidDisappear(bool animated) + { + base.ViewDidDisappear(animated); + ((TabbedPage)Element).SendDisappearing(); + } + + public override void ViewDidLayoutSubviews() + { + base.ViewDidLayoutSubviews(); + + if (Element == null) + return; + + if (!Element.Bounds.IsEmpty) + { + View.Frame = new System.Drawing.RectangleF((float)Element.X, (float)Element.Y, (float)Element.Width, (float)Element.Height); + } + + var frame = View.Frame; + var tabBarFrame = TabBar.Frame; + ((TabbedPage)Element).ContainerArea = new Rectangle(0, 0, frame.Width, frame.Height - tabBarFrame.Height); + + if (!_queuedSize.IsZero) + { + Element.Layout(new Rectangle(Element.X, Element.Y, _queuedSize.Width, _queuedSize.Height)); + _queuedSize = Size.Zero; + } + + _loaded = true; + } + + public override void ViewDidLoad() + { + base.ViewDidLoad(); + + if (!Forms.IsiOS7OrNewer) + WantsFullScreenLayout = false; + } + + protected override void Dispose(bool disposing) + { + if (disposing) + { + ((TabbedPage)Element).SendDisappearing(); + Tabbed.PropertyChanged -= OnPropertyChanged; + Tabbed.PagesChanged -= OnPagesChanged; + FinishedCustomizingViewControllers -= HandleFinishedCustomizingViewControllers; + } + + base.Dispose(disposing); + } + + protected virtual void OnElementChanged(VisualElementChangedEventArgs e) + { + var changed = ElementChanged; + if (changed != null) + changed(this, e); + } + + UIViewController GetViewController(Page page) + { + var renderer = Platform.GetRenderer(page); + if (renderer == null) + return null; + + return renderer.ViewController; + } + + void HandleFinishedCustomizingViewControllers(object sender, UITabBarCustomizeChangeEventArgs e) + { + if (e.Changed) + UpdateChildrenOrderIndex(e.ViewControllers); + } + + void OnPagePropertyChanged(object sender, PropertyChangedEventArgs e) + { + if (e.PropertyName == Page.TitleProperty.PropertyName) + { + var page = (Page)sender; + var renderer = Platform.GetRenderer(page); + if (renderer == null) + return; + + if (renderer.ViewController.TabBarItem != null) + renderer.ViewController.TabBarItem.Title = page.Title; + } + else if (e.PropertyName == Page.IconProperty.PropertyName) + { + var page = (Page)sender; + + var renderer = Platform.GetRenderer(page); + if (renderer == null) + return; + + if (renderer.ViewController.TabBarItem == null) + return; + + UIImage image = null; + if (!string.IsNullOrEmpty(page.Icon)) + image = new UIImage(page.Icon); + + // the new UITabBarItem forces redraw, setting the UITabBarItem.Image does not + renderer.ViewController.TabBarItem = new UITabBarItem(page.Title, image, 0); + + if (image != null) + image.Dispose(); + } + } + + void OnPagesChanged(object sender, NotifyCollectionChangedEventArgs e) + { + e.Apply((o, i, c) => SetupPage((Page)o, i), (o, i) => TeardownPage((Page)o, i), Reset); + + SetControllers(); + + UIViewController controller = null; + if (Tabbed.CurrentPage != null) + controller = GetViewController(Tabbed.CurrentPage); + if (controller != null && controller != base.SelectedViewController) + base.SelectedViewController = controller; + } + + void OnPropertyChanged(object sender, PropertyChangedEventArgs e) + { + if (e.PropertyName == "CurrentPage") + { + var current = Tabbed.CurrentPage; + if (current == null) + return; + + var controller = GetViewController(current); + if (controller == null) + return; + + SelectedViewController = controller; + } + } + + void Reset() + { + var i = 0; + foreach (var page in Tabbed.Children) + SetupPage(page, i++); + } + + void SetControllers() + { + var list = new List<UIViewController>(); + for (var i = 0; i < Element.LogicalChildren.Count; i++) + { + var child = Element.LogicalChildren[i]; + var v = child as VisualElement; + if (v == null) + continue; + if (Platform.GetRenderer(v) != null) + list.Add(Platform.GetRenderer(v).ViewController); + } + ViewControllers = list.ToArray(); + } + + void SetupPage(Page page, int index) + { + var renderer = Platform.GetRenderer(page); + if (renderer == null) + { + renderer = Platform.CreateRenderer(page); + Platform.SetRenderer(page, renderer); + } + + page.PropertyChanged += OnPagePropertyChanged; + + UIImage icon = null; + if (page.Icon != null) + icon = new UIImage(page.Icon); + + renderer.ViewController.TabBarItem = new UITabBarItem(page.Title, icon, 0); + if (icon != null) + icon.Dispose(); + + renderer.ViewController.TabBarItem.Tag = index; + } + + void TeardownPage(Page page, int index) + { + page.PropertyChanged -= OnPagePropertyChanged; + + Platform.SetRenderer(page, null); + } + + void UpdateChildrenOrderIndex(UIViewController[] viewControllers) + { + for (var i = 0; i < viewControllers.Length; i++) + { + var originalIndex = -1; + if (int.TryParse(viewControllers[i].TabBarItem.Tag.ToString(), out originalIndex)) + { + var page = (Page)Tabbed.InternalChildren[originalIndex]; + TabbedPage.SetIndex(page, i); + } + } + } + + void UpdateCurrentPage() + { + ((TabbedPage)Element).CurrentPage = SelectedIndex >= 0 && SelectedIndex < Tabbed.InternalChildren.Count ? Tabbed.GetPageByIndex((int)SelectedIndex) : null; + } + } +}
\ No newline at end of file diff --git a/Xamarin.Forms.Platform.iOS/Renderers/TableViewModelRenderer.cs b/Xamarin.Forms.Platform.iOS/Renderers/TableViewModelRenderer.cs new file mode 100644 index 00000000..0e9e15f4 --- /dev/null +++ b/Xamarin.Forms.Platform.iOS/Renderers/TableViewModelRenderer.cs @@ -0,0 +1,157 @@ +using System; +using System.Collections.Generic; +using Xamarin.Forms; +#if __UNIFIED__ +using UIKit; +using Foundation; +#else +using MonoTouch.UIKit; +using MonoTouch.Foundation; +#endif +#if __UNIFIED__ +using RectangleF = CoreGraphics.CGRect; +using SizeF = CoreGraphics.CGSize; +using PointF = CoreGraphics.CGPoint; + +#else +using nfloat=System.Single; +using nint=System.Int32; +using nuint=System.UInt32; +#endif + +namespace Xamarin.Forms.Platform.iOS +{ + public class TableViewModelRenderer : UITableViewSource + { + readonly Dictionary<nint, Cell> _headerCells = new Dictionary<nint, Cell>(); + + protected bool HasBoundGestures; + protected UITableView Table; + + protected TableView View; + + public TableViewModelRenderer(TableView model) + { + View = model; + View.ModelChanged += (s, e) => + { + if (Table != null) + Table.ReloadData(); + }; + AutomaticallyDeselect = true; + } + + public bool AutomaticallyDeselect { get; set; } + + public override UITableViewCell GetCell(UITableView tableView, NSIndexPath indexPath) + { + var cell = View.Model.GetCell(indexPath.Section, indexPath.Row); + + var nativeCell = CellTableViewCell.GetNativeCell(tableView, cell); + return nativeCell; + } + + public override nfloat GetHeightForHeader(UITableView tableView, nint section) + { + if (!_headerCells.ContainsKey((int)section)) + _headerCells[section] = View.Model.GetHeaderCell((int)section); + + var result = _headerCells[section]; + + return result == null ? UITableView.AutomaticDimension : (nfloat)result.Height; + } + + public override UIView GetViewForHeader(UITableView tableView, nint section) + { + if (!_headerCells.ContainsKey((int)section)) + _headerCells[section] = View.Model.GetHeaderCell((int)section); + + var result = _headerCells[section]; + + if (result != null) + { + var reusable = tableView.DequeueReusableCell(result.GetType().FullName); + + var cellRenderer = Registrar.Registered.GetHandler<CellRenderer>(result.GetType()); + return cellRenderer.GetCell(result, reusable, Table); + } + return null; + } + + public void LongPress(UILongPressGestureRecognizer gesture) + { + var point = gesture.LocationInView(Table); + var indexPath = Table.IndexPathForRowAtPoint(point); + if (indexPath == null) + return; + + View.Model.RowLongPressed(indexPath.Section, indexPath.Row); + } + + public override nint NumberOfSections(UITableView tableView) + { + BindGestures(tableView); + return View.Model.GetSectionCount(); + } + + public override void RowSelected(UITableView tableView, NSIndexPath indexPath) + { + View.Model.RowSelected(indexPath.Section, indexPath.Row); + if (AutomaticallyDeselect) + tableView.DeselectRow(indexPath, true); + } + + public override nint RowsInSection(UITableView tableview, nint section) + { + return View.Model.GetRowCount((int)section); + } + + public override string[] SectionIndexTitles(UITableView tableView) + { + return View.Model.GetSectionIndexTitles(); + } + + public override string TitleForHeader(UITableView tableView, nint section) + { + return View.Model.GetSectionTitle((int)section); + } + + void BindGestures(UITableView tableview) + { + if (HasBoundGestures) + return; + + HasBoundGestures = true; + + var gesture = new UILongPressGestureRecognizer(LongPress); + gesture.MinimumPressDuration = 2; + tableview.AddGestureRecognizer(gesture); + + var dismissGesture = new UITapGestureRecognizer(Tap); + dismissGesture.CancelsTouchesInView = false; + tableview.AddGestureRecognizer(dismissGesture); + + Table = tableview; + } + + void Tap(UITapGestureRecognizer gesture) + { + gesture.View.EndEditing(true); + } + } + + public class UnEvenTableViewModelRenderer : TableViewModelRenderer + { + public UnEvenTableViewModelRenderer(TableView model) : base(model) + { + } + + public override nfloat GetHeightForRow(UITableView tableView, NSIndexPath indexPath) + { + var h = View.Model.GetCell(indexPath.Section, indexPath.Row).Height; + if (h == -1) + return tableView.RowHeight; + return (nfloat)h; + } + } +}
\ No newline at end of file diff --git a/Xamarin.Forms.Platform.iOS/Renderers/TableViewRenderer.cs b/Xamarin.Forms.Platform.iOS/Renderers/TableViewRenderer.cs new file mode 100644 index 00000000..a77b32cc --- /dev/null +++ b/Xamarin.Forms.Platform.iOS/Renderers/TableViewRenderer.cs @@ -0,0 +1,150 @@ +using System; +using System.ComponentModel; +using System.Drawing; +using System.Collections.Generic; +#if __UNIFIED__ +using UIKit; +using RectangleF = CoreGraphics.CGRect; + +#else +using MonoTouch.UIKit; +#endif + +namespace Xamarin.Forms.Platform.iOS +{ + public class TableViewRenderer : ViewRenderer<TableView, UITableView> + { + KeyboardInsetTracker _insetTracker; + UIView _originalBackgroundView; + RectangleF _previousFrame; + + public override SizeRequest GetDesiredSize(double widthConstraint, double heightConstraint) + { + return Control.GetSizeRequest(widthConstraint, heightConstraint, 44, 44); + } + + public override void LayoutSubviews() + { + base.LayoutSubviews(); + + if (_previousFrame != Frame) + { + _previousFrame = Frame; + _insetTracker?.UpdateInsets(); + } + } + + protected override void Dispose(bool disposing) + { + if (disposing && _insetTracker != null) + { + _insetTracker.Dispose(); + _insetTracker = null; + + var viewsToLookAt = new Stack<UIView>(Subviews); + while (viewsToLookAt.Count > 0) + { + var view = viewsToLookAt.Pop(); + var viewCellRenderer = view as ViewCellRenderer.ViewTableCell; + if (viewCellRenderer != null) + viewCellRenderer.Dispose(); + else + { + foreach (var child in view.Subviews) + viewsToLookAt.Push(child); + } + } + } + + base.Dispose(disposing); + } + + protected override void OnElementChanged(ElementChangedEventArgs<TableView> e) + { + if (e.NewElement != null) + { + var style = UITableViewStyle.Plain; + if (e.NewElement.Intent != TableIntent.Data) + style = UITableViewStyle.Grouped; + + if (Control == null || Control.Style != style) + { + if (Control != null) + { + _insetTracker.Dispose(); + Control.Dispose(); + } + + var tv = new UITableView(RectangleF.Empty, style); + _originalBackgroundView = tv.BackgroundView; + + SetNativeControl(tv); + if (Forms.IsiOS9OrNewer) + tv.CellLayoutMarginsFollowReadableWidth = false; + + _insetTracker = new KeyboardInsetTracker(tv, () => Control.Window, insets => Control.ContentInset = Control.ScrollIndicatorInsets = insets, point => + { + var offset = Control.ContentOffset; + offset.Y += point.Y; + Control.SetContentOffset(offset, true); + }); + } + + SetSource(); + UpdateRowHeight(); + + UpdateBackgroundView(); + } + + base.OnElementChanged(e); + } + + protected override void OnElementPropertyChanged(object sender, PropertyChangedEventArgs e) + { + base.OnElementPropertyChanged(sender, e); + + if (e.PropertyName == "RowHeight") + UpdateRowHeight(); + else if (e.PropertyName == "HasUnevenRows") + SetSource(); + else if (e.PropertyName == "BackgroundColor") + UpdateBackgroundView(); + } + + protected override void UpdateNativeWidget() + { + if (Element.Opacity < 1) + { + if (!Control.Layer.ShouldRasterize) + { + Control.Layer.RasterizationScale = UIScreen.MainScreen.Scale; + Control.Layer.ShouldRasterize = true; + } + } + else + Control.Layer.ShouldRasterize = false; + base.UpdateNativeWidget(); + } + + void SetSource() + { + var modeledView = Element; + Control.Source = modeledView.HasUnevenRows ? new UnEvenTableViewModelRenderer(modeledView) : new TableViewModelRenderer(modeledView); + } + + void UpdateBackgroundView() + { + Control.BackgroundView = Element.BackgroundColor == Color.Default ? _originalBackgroundView : null; + } + + void UpdateRowHeight() + { + var height = Element.RowHeight; + + if (height <= 0) + height = 44; + + Control.RowHeight = height; + } + } +}
\ No newline at end of file diff --git a/Xamarin.Forms.Platform.iOS/Renderers/TabletMasterDetailRenderer.cs b/Xamarin.Forms.Platform.iOS/Renderers/TabletMasterDetailRenderer.cs new file mode 100644 index 00000000..3561322c --- /dev/null +++ b/Xamarin.Forms.Platform.iOS/Renderers/TabletMasterDetailRenderer.cs @@ -0,0 +1,371 @@ +using System; +using System.ComponentModel; +#if __UNIFIED__ +using UIKit; + +#else +using MonoTouch.UIKit; +#endif + +namespace Xamarin.Forms.Platform.iOS +{ + internal class ChildViewController : UIViewController + { + public override void ViewDidLayoutSubviews() + { + foreach (var vc in ChildViewControllers) + vc.View.Frame = View.Bounds; + } + } + + internal class EventedViewController : ChildViewController + { + public override void ViewWillAppear(bool animated) + { + base.ViewWillAppear(animated); + + var eh = WillAppear; + if (eh != null) + eh(this, EventArgs.Empty); + } + + public override void ViewWillDisappear(bool animated) + { + base.ViewWillDisappear(animated); + + var eh = WillDisappear; + if (eh != null) + eh(this, EventArgs.Empty); + } + + public event EventHandler WillAppear; + + public event EventHandler WillDisappear; + } + + public class TabletMasterDetailRenderer : UISplitViewController, IVisualElementRenderer + { + UIViewController _detailController; + + bool _disposed; + EventTracker _events; + InnerDelegate _innerDelegate; + + EventedViewController _masterController; + + MasterDetailPage _masterDetailPage; + + bool _masterVisible; + + VisualElementTracker _tracker; + + protected MasterDetailPage MasterDetailPage + { + get { return _masterDetailPage ?? (_masterDetailPage = (MasterDetailPage)Element); } + } + + UIBarButtonItem PresentButton + { + get { return _innerDelegate == null ? null : _innerDelegate.PresentButton; } + } + + public void Dispose() + { + if (_disposed) + return; + + if (Element != null) + { + ((Page)Element).SendDisappearing(); + Element.PropertyChanged -= HandlePropertyChanged; + Element = null; + } + + if (_tracker != null) + { + _tracker.Dispose(); + _tracker = null; + } + + if (_events != null) + { + _events.Dispose(); + _events = null; + } + + if (_masterController != null) + { + _masterController.WillAppear -= MasterControllerWillAppear; + _masterController.WillDisappear -= MasterControllerWillDisappear; + } + + _disposed = true; + } + + public VisualElement Element { get; private set; } + + public event EventHandler<VisualElementChangedEventArgs> ElementChanged; + + public SizeRequest GetDesiredSize(double widthConstraint, double heightConstraint) + { + return NativeView.GetSizeRequest(widthConstraint, heightConstraint); + } + + public UIView NativeView + { + get { return View; } + } + + public void SetElement(VisualElement element) + { + var oldElement = Element; + Element = element; + + ViewControllers = new[] { _masterController = new EventedViewController(), _detailController = new ChildViewController() }; + + Delegate = _innerDelegate = new InnerDelegate(MasterDetailPage.MasterBehavior); + + UpdateControllers(); + + _masterController.WillAppear += MasterControllerWillAppear; + _masterController.WillDisappear += MasterControllerWillDisappear; + + PresentsWithGesture = MasterDetailPage.IsGestureEnabled; + + OnElementChanged(new VisualElementChangedEventArgs(oldElement, element)); + + if (element != null) + element.SendViewInitialized(NativeView); + } + + public void SetElementSize(Size size) + { + Element.Layout(new Rectangle(Element.X, Element.Width, size.Width, size.Height)); + } + + public UIViewController ViewController + { + get { return this; } + } + + public override void ViewDidAppear(bool animated) + { + ((Page)Element).SendAppearing(); + base.ViewDidAppear(animated); + ToggleMaster(); + } + + public override void ViewDidDisappear(bool animated) + { + base.ViewDidDisappear(animated); + ((Page)Element).SendDisappearing(); + } + + public override void ViewDidLayoutSubviews() + { + base.ViewDidLayoutSubviews(); + + if (View.Subviews.Length < 2) + return; + + var detailsBounds = _detailController.View.Frame; + var masterBounds = _masterController.View.Frame; + + if (!masterBounds.IsEmpty) + MasterDetailPage.MasterBounds = new Rectangle(0, 0, masterBounds.Width, masterBounds.Height); + + if (!detailsBounds.IsEmpty) + MasterDetailPage.DetailBounds = new Rectangle(0, 0, detailsBounds.Width, detailsBounds.Height); + } + + public override void ViewDidLoad() + { + base.ViewDidLoad(); + UpdateBackground(); + _tracker = new VisualElementTracker(this); + _events = new EventTracker(this); + _events.LoadEvents(NativeView); + } + + public override void ViewWillDisappear(bool animated) + { + if (_masterVisible) + PerformButtonSelector(); + + base.ViewWillDisappear(animated); + } + + public override void ViewWillLayoutSubviews() + { + base.ViewWillLayoutSubviews(); + _masterController.View.BackgroundColor = UIColor.White; + } + + public override void WillRotate(UIInterfaceOrientation toInterfaceOrientation, double duration) + { + // On IOS8 the MasterViewController ViewAppear/Disappear weren't being called correctly after rotation + // We now close the Master by using the new SplitView API, basicly we set it to hidden and right back to the Normal/AutomaticMode + if (!MasterDetailPage.ShouldShowSplitMode && _masterVisible) + { + MasterDetailPage.CanChangeIsPresented = true; + if (Forms.IsiOS8OrNewer) + { + PreferredDisplayMode = UISplitViewControllerDisplayMode.PrimaryHidden; + PreferredDisplayMode = UISplitViewControllerDisplayMode.Automatic; + } + } + MasterDetailPage.UpdateMasterBehavior(MasterDetailPage); + MessagingCenter.Send<IVisualElementRenderer>(this, NavigationRenderer.UpdateToolbarButtons); + base.WillRotate(toInterfaceOrientation, duration); + } + + protected virtual void OnElementChanged(VisualElementChangedEventArgs e) + { + if (e.OldElement != null) + e.OldElement.PropertyChanged -= HandlePropertyChanged; + + if (e.NewElement != null) + e.NewElement.PropertyChanged += HandlePropertyChanged; + + var changed = ElementChanged; + if (changed != null) + changed(this, e); + } + + void ClearControllers() + { + foreach (var controller in _masterController.ChildViewControllers) + { + controller.View.RemoveFromSuperview(); + controller.RemoveFromParentViewController(); + } + + foreach (var controller in _detailController.ChildViewControllers) + { + controller.View.RemoveFromSuperview(); + controller.RemoveFromParentViewController(); + } + } + + void HandleMasterPropertyChanged(object sender, PropertyChangedEventArgs e) + { + if (e.PropertyName == Page.IconProperty.PropertyName || e.PropertyName == Page.TitleProperty.PropertyName) + MessagingCenter.Send<IVisualElementRenderer>(this, NavigationRenderer.UpdateToolbarButtons); + } + + void HandlePropertyChanged(object sender, PropertyChangedEventArgs e) + { + if (_tracker == null) + return; + + if (e.PropertyName == "Master" || e.PropertyName == "Detail") + UpdateControllers(); + else if (e.PropertyName == MasterDetailPage.IsPresentedProperty.PropertyName) + ToggleMaster(); + else if (e.PropertyName == MasterDetailPage.IsGestureEnabledProperty.PropertyName) + PresentsWithGesture = MasterDetailPage.IsGestureEnabled; + MessagingCenter.Send<IVisualElementRenderer>(this, NavigationRenderer.UpdateToolbarButtons); + } + + void MasterControllerWillAppear(object sender, EventArgs e) + { + _masterVisible = true; + if (MasterDetailPage.CanChangeIsPresented) + ((IElementController)Element).SetValueFromRenderer(MasterDetailPage.IsPresentedProperty, true); + } + + void MasterControllerWillDisappear(object sender, EventArgs e) + { + _masterVisible = false; + if (MasterDetailPage.CanChangeIsPresented) + ((IElementController)Element).SetValueFromRenderer(MasterDetailPage.IsPresentedProperty, false); + } + + void PerformButtonSelector() + { + if (Forms.IsiOS8OrNewer) + DisplayModeButtonItem.Target.PerformSelector(DisplayModeButtonItem.Action, DisplayModeButtonItem, 0); + else + PresentButton.Target.PerformSelector(PresentButton.Action, PresentButton, 0); + } + + void ToggleMaster() + { + if (_masterVisible == MasterDetailPage.IsPresented || MasterDetailPage.ShouldShowSplitMode) + return; + + PerformButtonSelector(); + } + + void UpdateBackground() + { + if (!string.IsNullOrEmpty(((Page)Element).BackgroundImage)) + View.BackgroundColor = UIColor.FromPatternImage(UIImage.FromBundle(((Page)Element).BackgroundImage)); + else if (Element.BackgroundColor == Color.Default) + View.BackgroundColor = UIColor.White; + else + View.BackgroundColor = Element.BackgroundColor.ToUIColor(); + } + + void UpdateControllers() + { + MasterDetailPage.Master.PropertyChanged -= HandleMasterPropertyChanged; + + if (Platform.GetRenderer(MasterDetailPage.Master) == null) + Platform.SetRenderer(MasterDetailPage.Master, Platform.CreateRenderer(MasterDetailPage.Master)); + if (Platform.GetRenderer(MasterDetailPage.Detail) == null) + Platform.SetRenderer(MasterDetailPage.Detail, Platform.CreateRenderer(MasterDetailPage.Detail)); + + ClearControllers(); + + MasterDetailPage.Master.PropertyChanged += HandleMasterPropertyChanged; + + var master = Platform.GetRenderer(MasterDetailPage.Master).ViewController; + var detail = Platform.GetRenderer(MasterDetailPage.Detail).ViewController; + + _masterController.View.AddSubview(master.View); + _masterController.AddChildViewController(master); + + _detailController.View.AddSubview(detail.View); + _detailController.AddChildViewController(detail); + } + + class InnerDelegate : UISplitViewControllerDelegate + { + readonly MasterBehavior _masterPresentedDefaultState; + + public InnerDelegate(MasterBehavior masterPresentedDefaultState) + { + _masterPresentedDefaultState = masterPresentedDefaultState; + } + + public UIBarButtonItem PresentButton { get; set; } + + public override bool ShouldHideViewController(UISplitViewController svc, UIViewController viewController, UIInterfaceOrientation inOrientation) + { + bool willHideViewController; + switch (_masterPresentedDefaultState) + { + case MasterBehavior.Split: + willHideViewController = false; + break; + case MasterBehavior.Popover: + willHideViewController = true; + break; + case MasterBehavior.SplitOnPortrait: + willHideViewController = !(inOrientation == UIInterfaceOrientation.Portrait || inOrientation == UIInterfaceOrientation.PortraitUpsideDown); + break; + default: + willHideViewController = inOrientation == UIInterfaceOrientation.Portrait || inOrientation == UIInterfaceOrientation.PortraitUpsideDown; + break; + } + return willHideViewController; + } + + public override void WillHideViewController(UISplitViewController svc, UIViewController aViewController, UIBarButtonItem barButtonItem, UIPopoverController pc) + { + PresentButton = barButtonItem; + } + } + } +}
\ No newline at end of file diff --git a/Xamarin.Forms.Platform.iOS/Renderers/TimePickerRenderer.cs b/Xamarin.Forms.Platform.iOS/Renderers/TimePickerRenderer.cs new file mode 100644 index 00000000..85d3ad35 --- /dev/null +++ b/Xamarin.Forms.Platform.iOS/Renderers/TimePickerRenderer.cs @@ -0,0 +1,104 @@ +using System; +using System.Drawing; +using System.ComponentModel; +#if __UNIFIED__ +using UIKit; +using Foundation; +#else +using MonoTouch.UIKit; +using MonoTouch.Foundation; +#endif +#if __UNIFIED__ +using RectangleF = CoreGraphics.CGRect; +using SizeF = CoreGraphics.CGSize; +using PointF = CoreGraphics.CGPoint; + +#else +using nfloat=System.Single; +using nint=System.Int32; +using nuint=System.UInt32; +#endif + +namespace Xamarin.Forms.Platform.iOS +{ + public class TimePickerRenderer : ViewRenderer<TimePicker, UITextField> + { + UIDatePicker _picker; + + protected override void Dispose(bool disposing) + { + if (disposing) + { + Control.Started -= OnStarted; + Control.Ended -= OnEnded; + + _picker.ValueChanged -= OnValueChanged; + } + + base.Dispose(disposing); + } + + protected override void OnElementChanged(ElementChangedEventArgs<TimePicker> e) + { + if (e.NewElement != null) + { + if (Control == null) + { + var entry = new NoCaretField { BorderStyle = UITextBorderStyle.RoundedRect }; + + entry.Started += OnStarted; + entry.Ended += OnEnded; + + _picker = new UIDatePicker { Mode = UIDatePickerMode.Time, TimeZone = new NSTimeZone("UTC") }; + + var width = UIScreen.MainScreen.Bounds.Width; + var toolbar = new UIToolbar(new RectangleF(0, 0, width, 44)) { BarStyle = UIBarStyle.Default, Translucent = true }; + var spacer = new UIBarButtonItem(UIBarButtonSystemItem.FlexibleSpace); + var doneButton = new UIBarButtonItem(UIBarButtonSystemItem.Done, (o, a) => entry.ResignFirstResponder()); + + toolbar.SetItems(new[] { spacer, doneButton }, false); + + entry.InputView = _picker; + entry.InputAccessoryView = toolbar; + + _picker.ValueChanged += OnValueChanged; + + SetNativeControl(entry); + } + + UpdateTime(); + } + + base.OnElementChanged(e); + } + + protected override void OnElementPropertyChanged(object sender, PropertyChangedEventArgs e) + { + base.OnElementPropertyChanged(sender, e); + + if (e.PropertyName == TimePicker.TimeProperty.PropertyName || e.PropertyName == TimePicker.FormatProperty.PropertyName) + UpdateTime(); + } + + void OnEnded(object sender, EventArgs eventArgs) + { + ((IElementController)Element).SetValueFromRenderer(VisualElement.IsFocusedPropertyKey, false); + } + + void OnStarted(object sender, EventArgs eventArgs) + { + ((IElementController)Element).SetValueFromRenderer(VisualElement.IsFocusedPropertyKey, true); + } + + void OnValueChanged(object sender, EventArgs e) + { + ((IElementController)Element).SetValueFromRenderer(TimePicker.TimeProperty, _picker.Date.ToDateTime() - new DateTime(1, 1, 1)); + } + + void UpdateTime() + { + _picker.Date = new DateTime(1, 1, 1).Add(Element.Time).ToNSDate(); + Control.Text = DateTime.Today.Add(Element.Time).ToString(Element.Format); + } + } +}
\ No newline at end of file diff --git a/Xamarin.Forms.Platform.iOS/Renderers/ToolbarRenderer.cs b/Xamarin.Forms.Platform.iOS/Renderers/ToolbarRenderer.cs new file mode 100644 index 00000000..705f95d8 --- /dev/null +++ b/Xamarin.Forms.Platform.iOS/Renderers/ToolbarRenderer.cs @@ -0,0 +1,66 @@ +using System.Linq; +using System.Drawing; +#if __UNIFIED__ +using UIKit; + +#else +using MonoTouch.UIKit; +#endif + +namespace Xamarin.Forms.Platform.iOS +{ + public class ToolbarRenderer : ViewRenderer + { + protected override void Dispose(bool disposing) + { + if (disposing) + { + ((Toolbar)Element).ItemAdded -= OnItemAdded; + ((Toolbar)Element).ItemRemoved -= OnItemRemoved; + + if (((UIToolbar)Control).Items != null) + { + foreach (var item in ((UIToolbar)Control).Items) + item.Dispose(); + } + } + + base.Dispose(disposing); + } + + protected override void OnElementChanged(ElementChangedEventArgs<View> e) + { + base.OnElementChanged(e); + + SetNativeControl(new UIToolbar(RectangleF.Empty)); + UpdateItems(false); + + ((Toolbar)Element).ItemAdded += OnItemAdded; + ((Toolbar)Element).ItemRemoved += OnItemRemoved; + } + + void OnItemAdded(object sender, ToolbarItemEventArgs eventArg) + { + UpdateItems(true); + } + + void OnItemRemoved(object sender, ToolbarItemEventArgs eventArg) + { + UpdateItems(true); + } + + void UpdateItems(bool animated) + { + if (((UIToolbar)Control).Items != null) + { + for (var i = 0; i < ((UIToolbar)Control).Items.Length; i++) + ((UIToolbar)Control).Items[i].Dispose(); + } + var items = new UIBarButtonItem[((Toolbar)Element).Items.Count]; + for (var i = 0; i < ((Toolbar)Element).Items.Count; i++) + items[i] = ((Toolbar)Element).Items[i].ToUIBarButtonItem(); + + ((UIToolbar)Control).SetItems(items, animated); + } + } +}
\ No newline at end of file diff --git a/Xamarin.Forms.Platform.iOS/Renderers/WebViewRenderer.cs b/Xamarin.Forms.Platform.iOS/Renderers/WebViewRenderer.cs new file mode 100644 index 00000000..538bc2d0 --- /dev/null +++ b/Xamarin.Forms.Platform.iOS/Renderers/WebViewRenderer.cs @@ -0,0 +1,270 @@ +using System; +using System.Drawing; +using System.ComponentModel; +#if __UNIFIED__ +using UIKit; +using Foundation; + +#else +using MonoTouch.UIKit; +using MonoTouch.Foundation; +#endif + +namespace Xamarin.Forms.Platform.iOS +{ + public class WebViewRenderer : UIWebView, IVisualElementRenderer, IWebViewRenderer + { + EventTracker _events; + bool _ignoreSourceChanges; + WebNavigationEvent _lastBackForwardEvent; + VisualElementPackager _packager; + + VisualElementTracker _tracker; + + public WebViewRenderer() : base(RectangleF.Empty) + { + } + + public VisualElement Element { get; private set; } + + public event EventHandler<VisualElementChangedEventArgs> ElementChanged; + + public SizeRequest GetDesiredSize(double widthConstraint, double heightConstraint) + { + return NativeView.GetSizeRequest(widthConstraint, heightConstraint, 44, 44); + } + + public void SetElement(VisualElement element) + { + var oldElement = Element; + Element = element; + Element.PropertyChanged += HandlePropertyChanged; + ((WebView)Element).EvalRequested += OnEvalRequested; + ((WebView)Element).GoBackRequested += OnGoBackRequested; + ((WebView)Element).GoForwardRequested += OnGoForwardRequested; + Delegate = new CustomWebViewDelegate(this); + + BackgroundColor = UIColor.Clear; + + AutosizesSubviews = true; + AutoresizingMask = UIViewAutoresizing.FlexibleWidth | UIViewAutoresizing.FlexibleHeight; + + _tracker = new VisualElementTracker(this); + + _packager = new VisualElementPackager(this); + _packager.Load(); + + _events = new EventTracker(this); + _events.LoadEvents(this); + + Load(); + + OnElementChanged(new VisualElementChangedEventArgs(oldElement, element)); + + if (Element != null && !string.IsNullOrEmpty(Element.AutomationId)) + AccessibilityIdentifier = Element.AutomationId; + + if (element != null) + element.SendViewInitialized(this); + + if (Element != null && !string.IsNullOrEmpty(Element.AutomationId)) + AccessibilityIdentifier = Element.AutomationId; + } + + public void SetElementSize(Size size) + { + Layout.LayoutChildIntoBoundingRegion(Element, new Rectangle(Element.X, Element.Y, size.Width, size.Height)); + } + + public void LoadHtml(string html, string baseUrl) + { + if (html != null) + LoadHtmlString(html, baseUrl == null ? new NSUrl(NSBundle.MainBundle.BundlePath, true) : new NSUrl(baseUrl, true)); + } + + public void LoadUrl(string url) + { + LoadRequest(new NSUrlRequest(new NSUrl(url))); + } + + public override void LayoutSubviews() + { + base.LayoutSubviews(); + + // ensure that inner scrollview properly resizes when frame of webview updated + ScrollView.Frame = Bounds; + } + + protected override void Dispose(bool disposing) + { + if (disposing) + { + if (IsLoading) + StopLoading(); + + Element.PropertyChanged -= HandlePropertyChanged; + ((WebView)Element).EvalRequested -= OnEvalRequested; + ((WebView)Element).GoBackRequested -= OnGoBackRequested; + ((WebView)Element).GoForwardRequested -= OnGoForwardRequested; + } + + base.Dispose(disposing); + } + + protected virtual void OnElementChanged(VisualElementChangedEventArgs e) + { + var changed = ElementChanged; + if (changed != null) + changed(this, e); + } + + void HandlePropertyChanged(object sender, PropertyChangedEventArgs e) + { + if (e.PropertyName == WebView.SourceProperty.PropertyName) + Load(); + } + + void Load() + { + if (_ignoreSourceChanges) + return; + + if (((WebView)Element).Source != null) + ((WebView)Element).Source.Load(this); + + UpdateCanGoBackForward(); + } + + void OnEvalRequested(object sender, EventArg<string> eventArg) + { + EvaluateJavascript(eventArg.Data); + } + + void OnGoBackRequested(object sender, EventArgs eventArgs) + { + if (CanGoBack) + { + _lastBackForwardEvent = WebNavigationEvent.Back; + GoBack(); + } + + UpdateCanGoBackForward(); + } + + void OnGoForwardRequested(object sender, EventArgs eventArgs) + { + if (CanGoForward) + { + _lastBackForwardEvent = WebNavigationEvent.Forward; + GoForward(); + } + + UpdateCanGoBackForward(); + } + + void UpdateCanGoBackForward() + { + ((WebView)Element).CanGoBack = CanGoBack; + ((WebView)Element).CanGoForward = CanGoForward; + } + + class CustomWebViewDelegate : UIWebViewDelegate + { + readonly WebViewRenderer _renderer; + WebNavigationEvent _lastEvent; + + public CustomWebViewDelegate(WebViewRenderer renderer) + { + if (renderer == null) + throw new ArgumentNullException("renderer"); + _renderer = renderer; + } + + WebView WebView + { + get { return (WebView)_renderer.Element; } + } + + public override void LoadFailed(UIWebView webView, NSError error) + { + var url = GetCurrentUrl(); + WebView.SendNavigated(new WebNavigatedEventArgs(_lastEvent, new UrlWebViewSource { Url = url }, url, WebNavigationResult.Failure)); + + _renderer.UpdateCanGoBackForward(); + } + + public override void LoadingFinished(UIWebView webView) + { + if (webView.IsLoading) + return; + + _renderer._ignoreSourceChanges = true; + var url = GetCurrentUrl(); + ((IElementController)WebView).SetValueFromRenderer(WebView.SourceProperty, new UrlWebViewSource { Url = url }); + _renderer._ignoreSourceChanges = false; + + var args = new WebNavigatedEventArgs(_lastEvent, WebView.Source, url, WebNavigationResult.Success); + WebView.SendNavigated(args); + + _renderer.UpdateCanGoBackForward(); + } + + public override void LoadStarted(UIWebView webView) + { + } + + public override bool ShouldStartLoad(UIWebView webView, NSUrlRequest request, UIWebViewNavigationType navigationType) + { + var navEvent = WebNavigationEvent.NewPage; + switch (navigationType) + { + case UIWebViewNavigationType.LinkClicked: + navEvent = WebNavigationEvent.NewPage; + break; + case UIWebViewNavigationType.FormSubmitted: + navEvent = WebNavigationEvent.NewPage; + break; + case UIWebViewNavigationType.BackForward: + navEvent = _renderer._lastBackForwardEvent; + break; + case UIWebViewNavigationType.Reload: + navEvent = WebNavigationEvent.Refresh; + break; + case UIWebViewNavigationType.FormResubmitted: + navEvent = WebNavigationEvent.NewPage; + break; + case UIWebViewNavigationType.Other: + navEvent = WebNavigationEvent.NewPage; + break; + } + + _lastEvent = navEvent; + var lastUrl = request.Url.ToString(); + var args = new WebNavigatingEventArgs(navEvent, new UrlWebViewSource { Url = lastUrl }, lastUrl); + + WebView.SendNavigating(args); + _renderer.UpdateCanGoBackForward(); + return !args.Cancel; + } + + string GetCurrentUrl() + { + return _renderer?.Request?.Url?.AbsoluteUrl?.ToString(); + } + } + + #region IPlatformRenderer implementation + + public UIView NativeView + { + get { return this; } + } + + public UIViewController ViewController + { + get { return null; } + } + + #endregion + } +}
\ No newline at end of file diff --git a/Xamarin.Forms.Platform.iOS/Resources/StringResources.Designer.cs b/Xamarin.Forms.Platform.iOS/Resources/StringResources.Designer.cs new file mode 100644 index 00000000..096da604 --- /dev/null +++ b/Xamarin.Forms.Platform.iOS/Resources/StringResources.Designer.cs @@ -0,0 +1,81 @@ +//------------------------------------------------------------------------------ +// <auto-generated> +// This code was generated by a tool. +// Runtime Version:4.0.30319.42000 +// +// Changes to this file may cause incorrect behavior and will be lost if +// the code is regenerated. +// </auto-generated> +//------------------------------------------------------------------------------ + +namespace Xamarin.Forms.Platform.iOS.Resources { + using System; + + + /// <summary> + /// A strongly-typed resource class, for looking up localized strings, etc. + /// </summary> + // This class was auto-generated by the StronglyTypedResourceBuilder + // class via a tool like ResGen or Visual Studio. + // To add or remove a member, edit your .ResX file then rerun ResGen + // with the /str option, or rebuild your VS project. + [global::System.CodeDom.Compiler.GeneratedCodeAttribute("System.Resources.Tools.StronglyTypedResourceBuilder", "4.0.0.0")] + [global::System.Diagnostics.DebuggerNonUserCodeAttribute()] + [global::System.Runtime.CompilerServices.CompilerGeneratedAttribute()] + internal class StringResources { + + private static global::System.Resources.ResourceManager resourceMan; + + private static global::System.Globalization.CultureInfo resourceCulture; + + [global::System.Diagnostics.CodeAnalysis.SuppressMessageAttribute("Microsoft.Performance", "CA1811:AvoidUncalledPrivateCode")] + internal StringResources() { + } + + /// <summary> + /// Returns the cached ResourceManager instance used by this class. + /// </summary> + [global::System.ComponentModel.EditorBrowsableAttribute(global::System.ComponentModel.EditorBrowsableState.Advanced)] + internal static global::System.Resources.ResourceManager ResourceManager { + get { + if (object.ReferenceEquals(resourceMan, null)) { + global::System.Resources.ResourceManager temp = new global::System.Resources.ResourceManager("Xamarin.Forms.Platform.iOS.Resources.StringResources", typeof(StringResources).Assembly); + resourceMan = temp; + } + return resourceMan; + } + } + + /// <summary> + /// Overrides the current thread's CurrentUICulture property for all + /// resource lookups using this strongly typed resource class. + /// </summary> + [global::System.ComponentModel.EditorBrowsableAttribute(global::System.ComponentModel.EditorBrowsableState.Advanced)] + internal static global::System.Globalization.CultureInfo Culture { + get { + return resourceCulture; + } + set { + resourceCulture = value; + } + } + + /// <summary> + /// Looks up a localized string similar to Cancel. + /// </summary> + internal static string Cancel { + get { + return ResourceManager.GetString("Cancel", resourceCulture); + } + } + + /// <summary> + /// Looks up a localized string similar to More. + /// </summary> + internal static string More { + get { + return ResourceManager.GetString("More", resourceCulture); + } + } + } +} diff --git a/Xamarin.Forms.Platform.iOS/Resources/StringResources.ar.resx b/Xamarin.Forms.Platform.iOS/Resources/StringResources.ar.resx new file mode 100644 index 00000000..2382ee59 --- /dev/null +++ b/Xamarin.Forms.Platform.iOS/Resources/StringResources.ar.resx @@ -0,0 +1,128 @@ +<?xml version="1.0" encoding="utf-8"?> +<root> + <!-- + Microsoft ResX Schema + + Version 2.0 + + The primary goals of this format is to allow a simple XML format + that is mostly human readable. The generation and parsing of the + various data types are done through the TypeConverter classes + associated with the data types. + + Example: + + ... ado.net/XML headers & schema ... + <resheader name="resmimetype">text/microsoft-resx</resheader> + <resheader name="version">2.0</resheader> + <resheader name="reader">System.Resources.ResXResourceReader, System.Windows.Forms, ...</resheader> + <resheader name="writer">System.Resources.ResXResourceWriter, System.Windows.Forms, ...</resheader> + <data name="Name1"><value>this is my long string</value><comment>this is a comment</comment></data> + <data name="Color1" type="System.Drawing.Color, System.Drawing">Blue</data> + <data name="Bitmap1" mimetype="application/x-microsoft.net.object.binary.base64"> + <value>[base64 mime encoded serialized .NET Framework object]</value> + </data> + <data name="Icon1" type="System.Drawing.Icon, System.Drawing" mimetype="application/x-microsoft.net.object.bytearray.base64"> + <value>[base64 mime encoded string representing a byte array form of the .NET Framework object]</value> + <comment>This is a comment</comment> + </data> + + There are any number of "resheader" rows that contain simple + name/value pairs. + + Each data row contains a name, and value. The row also contains a + type or mimetype. Type corresponds to a .NET class that support + text/value conversion through the TypeConverter architecture. + Classes that don't support this are serialized and stored with the + mimetype set. + + The mimetype is used for serialized objects, and tells the + ResXResourceReader how to depersist the object. This is currently not + extensible. For a given mimetype the value must be set accordingly: + + Note - application/x-microsoft.net.object.binary.base64 is the format + that the ResXResourceWriter will generate, however the reader can + read any of the formats listed below. + + mimetype: application/x-microsoft.net.object.binary.base64 + value : The object must be serialized with + : System.Runtime.Serialization.Formatters.Binary.BinaryFormatter + : and then encoded with base64 encoding. + + mimetype: application/x-microsoft.net.object.soap.base64 + value : The object must be serialized with + : System.Runtime.Serialization.Formatters.Soap.SoapFormatter + : and then encoded with base64 encoding. + + mimetype: application/x-microsoft.net.object.bytearray.base64 + value : The object must be serialized into a byte array + : using a System.ComponentModel.TypeConverter + : and then encoded with base64 encoding. + --> + <xsd:schema id="root" xmlns="" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:msdata="urn:schemas-microsoft-com:xml-msdata"> + <xsd:import namespace="http://www.w3.org/XML/1998/namespace" /> + <xsd:element name="root" msdata:IsDataSet="true"> + <xsd:complexType> + <xsd:choice maxOccurs="unbounded"> + <xsd:element name="metadata"> + <xsd:complexType> + <xsd:sequence> + <xsd:element name="value" type="xsd:string" minOccurs="0" /> + </xsd:sequence> + <xsd:attribute name="name" use="required" type="xsd:string" /> + <xsd:attribute name="type" type="xsd:string" /> + <xsd:attribute name="mimetype" type="xsd:string" /> + <xsd:attribute ref="xml:space" /> + </xsd:complexType> + </xsd:element> + <xsd:element name="assembly"> + <xsd:complexType> + <xsd:attribute name="alias" type="xsd:string" /> + <xsd:attribute name="name" type="xsd:string" /> + </xsd:complexType> + </xsd:element> + <xsd:element name="data"> + <xsd:complexType> + <xsd:sequence> + <xsd:element name="value" type="xsd:string" minOccurs="0" msdata:Ordinal="1" /> + <xsd:element name="comment" type="xsd:string" minOccurs="0" msdata:Ordinal="2" /> + </xsd:sequence> + <xsd:attribute name="name" type="xsd:string" use="required" msdata:Ordinal="1" /> + <xsd:attribute name="type" type="xsd:string" msdata:Ordinal="3" /> + <xsd:attribute name="mimetype" type="xsd:string" msdata:Ordinal="4" /> + <xsd:attribute ref="xml:space" /> + </xsd:complexType> + </xsd:element> + <xsd:element name="resheader"> + <xsd:complexType> + <xsd:sequence> + <xsd:element name="value" type="xsd:string" minOccurs="0" msdata:Ordinal="1" /> + </xsd:sequence> + <xsd:attribute name="name" type="xsd:string" use="required" /> + </xsd:complexType> + </xsd:element> + </xsd:choice> + </xsd:complexType> + </xsd:element> + </xsd:schema> + <resheader name="resmimetype"> + <value>text/microsoft-resx</value> + </resheader> + <resheader name="version"> + <value>2.0</value> + </resheader> + <resheader name="reader"> + <value>System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value> + </resheader> + <resheader name="writer"> + <value>System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value> + </resheader> + <data name="Cancel" xml:space="preserve"> + <value>إلخاء</value> + <comment>ContextActionCell Cancel button</comment> + </data> + <data name="More" xml:space="preserve"> + <value>إضافي</value> + <comment>ContextActionCell More button</comment> + </data> +</root>
\ No newline at end of file diff --git a/Xamarin.Forms.Platform.iOS/Resources/StringResources.ca.resx b/Xamarin.Forms.Platform.iOS/Resources/StringResources.ca.resx new file mode 100644 index 00000000..f8c683b4 --- /dev/null +++ b/Xamarin.Forms.Platform.iOS/Resources/StringResources.ca.resx @@ -0,0 +1,128 @@ +<?xml version="1.0" encoding="utf-8"?> +<root> + <!-- + Microsoft ResX Schema + + Version 2.0 + + The primary goals of this format is to allow a simple XML format + that is mostly human readable. The generation and parsing of the + various data types are done through the TypeConverter classes + associated with the data types. + + Example: + + ... ado.net/XML headers & schema ... + <resheader name="resmimetype">text/microsoft-resx</resheader> + <resheader name="version">2.0</resheader> + <resheader name="reader">System.Resources.ResXResourceReader, System.Windows.Forms, ...</resheader> + <resheader name="writer">System.Resources.ResXResourceWriter, System.Windows.Forms, ...</resheader> + <data name="Name1"><value>this is my long string</value><comment>this is a comment</comment></data> + <data name="Color1" type="System.Drawing.Color, System.Drawing">Blue</data> + <data name="Bitmap1" mimetype="application/x-microsoft.net.object.binary.base64"> + <value>[base64 mime encoded serialized .NET Framework object]</value> + </data> + <data name="Icon1" type="System.Drawing.Icon, System.Drawing" mimetype="application/x-microsoft.net.object.bytearray.base64"> + <value>[base64 mime encoded string representing a byte array form of the .NET Framework object]</value> + <comment>This is a comment</comment> + </data> + + There are any number of "resheader" rows that contain simple + name/value pairs. + + Each data row contains a name, and value. The row also contains a + type or mimetype. Type corresponds to a .NET class that support + text/value conversion through the TypeConverter architecture. + Classes that don't support this are serialized and stored with the + mimetype set. + + The mimetype is used for serialized objects, and tells the + ResXResourceReader how to depersist the object. This is currently not + extensible. For a given mimetype the value must be set accordingly: + + Note - application/x-microsoft.net.object.binary.base64 is the format + that the ResXResourceWriter will generate, however the reader can + read any of the formats listed below. + + mimetype: application/x-microsoft.net.object.binary.base64 + value : The object must be serialized with + : System.Runtime.Serialization.Formatters.Binary.BinaryFormatter + : and then encoded with base64 encoding. + + mimetype: application/x-microsoft.net.object.soap.base64 + value : The object must be serialized with + : System.Runtime.Serialization.Formatters.Soap.SoapFormatter + : and then encoded with base64 encoding. + + mimetype: application/x-microsoft.net.object.bytearray.base64 + value : The object must be serialized into a byte array + : using a System.ComponentModel.TypeConverter + : and then encoded with base64 encoding. + --> + <xsd:schema id="root" xmlns="" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:msdata="urn:schemas-microsoft-com:xml-msdata"> + <xsd:import namespace="http://www.w3.org/XML/1998/namespace" /> + <xsd:element name="root" msdata:IsDataSet="true"> + <xsd:complexType> + <xsd:choice maxOccurs="unbounded"> + <xsd:element name="metadata"> + <xsd:complexType> + <xsd:sequence> + <xsd:element name="value" type="xsd:string" minOccurs="0" /> + </xsd:sequence> + <xsd:attribute name="name" use="required" type="xsd:string" /> + <xsd:attribute name="type" type="xsd:string" /> + <xsd:attribute name="mimetype" type="xsd:string" /> + <xsd:attribute ref="xml:space" /> + </xsd:complexType> + </xsd:element> + <xsd:element name="assembly"> + <xsd:complexType> + <xsd:attribute name="alias" type="xsd:string" /> + <xsd:attribute name="name" type="xsd:string" /> + </xsd:complexType> + </xsd:element> + <xsd:element name="data"> + <xsd:complexType> + <xsd:sequence> + <xsd:element name="value" type="xsd:string" minOccurs="0" msdata:Ordinal="1" /> + <xsd:element name="comment" type="xsd:string" minOccurs="0" msdata:Ordinal="2" /> + </xsd:sequence> + <xsd:attribute name="name" type="xsd:string" use="required" msdata:Ordinal="1" /> + <xsd:attribute name="type" type="xsd:string" msdata:Ordinal="3" /> + <xsd:attribute name="mimetype" type="xsd:string" msdata:Ordinal="4" /> + <xsd:attribute ref="xml:space" /> + </xsd:complexType> + </xsd:element> + <xsd:element name="resheader"> + <xsd:complexType> + <xsd:sequence> + <xsd:element name="value" type="xsd:string" minOccurs="0" msdata:Ordinal="1" /> + </xsd:sequence> + <xsd:attribute name="name" type="xsd:string" use="required" /> + </xsd:complexType> + </xsd:element> + </xsd:choice> + </xsd:complexType> + </xsd:element> + </xsd:schema> + <resheader name="resmimetype"> + <value>text/microsoft-resx</value> + </resheader> + <resheader name="version"> + <value>2.0</value> + </resheader> + <resheader name="reader"> + <value>System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value> + </resheader> + <resheader name="writer"> + <value>System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value> + </resheader> + <data name="Cancel" xml:space="preserve"> + <value>Cancel·lar</value> + <comment>ContextActionCell Cancel button</comment> + </data> + <data name="More" xml:space="preserve"> + <value>Més</value> + <comment>ContextActionCell More button</comment> + </data> +</root>
\ No newline at end of file diff --git a/Xamarin.Forms.Platform.iOS/Resources/StringResources.cs.resx b/Xamarin.Forms.Platform.iOS/Resources/StringResources.cs.resx new file mode 100644 index 00000000..55c6946a --- /dev/null +++ b/Xamarin.Forms.Platform.iOS/Resources/StringResources.cs.resx @@ -0,0 +1,128 @@ +<?xml version="1.0" encoding="utf-8"?> +<root> + <!-- + Microsoft ResX Schema + + Version 2.0 + + The primary goals of this format is to allow a simple XML format + that is mostly human readable. The generation and parsing of the + various data types are done through the TypeConverter classes + associated with the data types. + + Example: + + ... ado.net/XML headers & schema ... + <resheader name="resmimetype">text/microsoft-resx</resheader> + <resheader name="version">2.0</resheader> + <resheader name="reader">System.Resources.ResXResourceReader, System.Windows.Forms, ...</resheader> + <resheader name="writer">System.Resources.ResXResourceWriter, System.Windows.Forms, ...</resheader> + <data name="Name1"><value>this is my long string</value><comment>this is a comment</comment></data> + <data name="Color1" type="System.Drawing.Color, System.Drawing">Blue</data> + <data name="Bitmap1" mimetype="application/x-microsoft.net.object.binary.base64"> + <value>[base64 mime encoded serialized .NET Framework object]</value> + </data> + <data name="Icon1" type="System.Drawing.Icon, System.Drawing" mimetype="application/x-microsoft.net.object.bytearray.base64"> + <value>[base64 mime encoded string representing a byte array form of the .NET Framework object]</value> + <comment>This is a comment</comment> + </data> + + There are any number of "resheader" rows that contain simple + name/value pairs. + + Each data row contains a name, and value. The row also contains a + type or mimetype. Type corresponds to a .NET class that support + text/value conversion through the TypeConverter architecture. + Classes that don't support this are serialized and stored with the + mimetype set. + + The mimetype is used for serialized objects, and tells the + ResXResourceReader how to depersist the object. This is currently not + extensible. For a given mimetype the value must be set accordingly: + + Note - application/x-microsoft.net.object.binary.base64 is the format + that the ResXResourceWriter will generate, however the reader can + read any of the formats listed below. + + mimetype: application/x-microsoft.net.object.binary.base64 + value : The object must be serialized with + : System.Runtime.Serialization.Formatters.Binary.BinaryFormatter + : and then encoded with base64 encoding. + + mimetype: application/x-microsoft.net.object.soap.base64 + value : The object must be serialized with + : System.Runtime.Serialization.Formatters.Soap.SoapFormatter + : and then encoded with base64 encoding. + + mimetype: application/x-microsoft.net.object.bytearray.base64 + value : The object must be serialized into a byte array + : using a System.ComponentModel.TypeConverter + : and then encoded with base64 encoding. + --> + <xsd:schema id="root" xmlns="" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:msdata="urn:schemas-microsoft-com:xml-msdata"> + <xsd:import namespace="http://www.w3.org/XML/1998/namespace" /> + <xsd:element name="root" msdata:IsDataSet="true"> + <xsd:complexType> + <xsd:choice maxOccurs="unbounded"> + <xsd:element name="metadata"> + <xsd:complexType> + <xsd:sequence> + <xsd:element name="value" type="xsd:string" minOccurs="0" /> + </xsd:sequence> + <xsd:attribute name="name" use="required" type="xsd:string" /> + <xsd:attribute name="type" type="xsd:string" /> + <xsd:attribute name="mimetype" type="xsd:string" /> + <xsd:attribute ref="xml:space" /> + </xsd:complexType> + </xsd:element> + <xsd:element name="assembly"> + <xsd:complexType> + <xsd:attribute name="alias" type="xsd:string" /> + <xsd:attribute name="name" type="xsd:string" /> + </xsd:complexType> + </xsd:element> + <xsd:element name="data"> + <xsd:complexType> + <xsd:sequence> + <xsd:element name="value" type="xsd:string" minOccurs="0" msdata:Ordinal="1" /> + <xsd:element name="comment" type="xsd:string" minOccurs="0" msdata:Ordinal="2" /> + </xsd:sequence> + <xsd:attribute name="name" type="xsd:string" use="required" msdata:Ordinal="1" /> + <xsd:attribute name="type" type="xsd:string" msdata:Ordinal="3" /> + <xsd:attribute name="mimetype" type="xsd:string" msdata:Ordinal="4" /> + <xsd:attribute ref="xml:space" /> + </xsd:complexType> + </xsd:element> + <xsd:element name="resheader"> + <xsd:complexType> + <xsd:sequence> + <xsd:element name="value" type="xsd:string" minOccurs="0" msdata:Ordinal="1" /> + </xsd:sequence> + <xsd:attribute name="name" type="xsd:string" use="required" /> + </xsd:complexType> + </xsd:element> + </xsd:choice> + </xsd:complexType> + </xsd:element> + </xsd:schema> + <resheader name="resmimetype"> + <value>text/microsoft-resx</value> + </resheader> + <resheader name="version"> + <value>2.0</value> + </resheader> + <resheader name="reader"> + <value>System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value> + </resheader> + <resheader name="writer"> + <value>System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value> + </resheader> + <data name="Cancel" xml:space="preserve"> + <value>Zrušit</value> + <comment>ContextActionCell Cancel button</comment> + </data> + <data name="More" xml:space="preserve"> + <value>Další</value> + <comment>ContextActionCell More button</comment> + </data> +</root>
\ No newline at end of file diff --git a/Xamarin.Forms.Platform.iOS/Resources/StringResources.da.resx b/Xamarin.Forms.Platform.iOS/Resources/StringResources.da.resx new file mode 100644 index 00000000..0ff5a6fa --- /dev/null +++ b/Xamarin.Forms.Platform.iOS/Resources/StringResources.da.resx @@ -0,0 +1,128 @@ +<?xml version="1.0" encoding="utf-8"?> +<root> + <!-- + Microsoft ResX Schema + + Version 2.0 + + The primary goals of this format is to allow a simple XML format + that is mostly human readable. The generation and parsing of the + various data types are done through the TypeConverter classes + associated with the data types. + + Example: + + ... ado.net/XML headers & schema ... + <resheader name="resmimetype">text/microsoft-resx</resheader> + <resheader name="version">2.0</resheader> + <resheader name="reader">System.Resources.ResXResourceReader, System.Windows.Forms, ...</resheader> + <resheader name="writer">System.Resources.ResXResourceWriter, System.Windows.Forms, ...</resheader> + <data name="Name1"><value>this is my long string</value><comment>this is a comment</comment></data> + <data name="Color1" type="System.Drawing.Color, System.Drawing">Blue</data> + <data name="Bitmap1" mimetype="application/x-microsoft.net.object.binary.base64"> + <value>[base64 mime encoded serialized .NET Framework object]</value> + </data> + <data name="Icon1" type="System.Drawing.Icon, System.Drawing" mimetype="application/x-microsoft.net.object.bytearray.base64"> + <value>[base64 mime encoded string representing a byte array form of the .NET Framework object]</value> + <comment>This is a comment</comment> + </data> + + There are any number of "resheader" rows that contain simple + name/value pairs. + + Each data row contains a name, and value. The row also contains a + type or mimetype. Type corresponds to a .NET class that support + text/value conversion through the TypeConverter architecture. + Classes that don't support this are serialized and stored with the + mimetype set. + + The mimetype is used for serialized objects, and tells the + ResXResourceReader how to depersist the object. This is currently not + extensible. For a given mimetype the value must be set accordingly: + + Note - application/x-microsoft.net.object.binary.base64 is the format + that the ResXResourceWriter will generate, however the reader can + read any of the formats listed below. + + mimetype: application/x-microsoft.net.object.binary.base64 + value : The object must be serialized with + : System.Runtime.Serialization.Formatters.Binary.BinaryFormatter + : and then encoded with base64 encoding. + + mimetype: application/x-microsoft.net.object.soap.base64 + value : The object must be serialized with + : System.Runtime.Serialization.Formatters.Soap.SoapFormatter + : and then encoded with base64 encoding. + + mimetype: application/x-microsoft.net.object.bytearray.base64 + value : The object must be serialized into a byte array + : using a System.ComponentModel.TypeConverter + : and then encoded with base64 encoding. + --> + <xsd:schema id="root" xmlns="" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:msdata="urn:schemas-microsoft-com:xml-msdata"> + <xsd:import namespace="http://www.w3.org/XML/1998/namespace" /> + <xsd:element name="root" msdata:IsDataSet="true"> + <xsd:complexType> + <xsd:choice maxOccurs="unbounded"> + <xsd:element name="metadata"> + <xsd:complexType> + <xsd:sequence> + <xsd:element name="value" type="xsd:string" minOccurs="0" /> + </xsd:sequence> + <xsd:attribute name="name" use="required" type="xsd:string" /> + <xsd:attribute name="type" type="xsd:string" /> + <xsd:attribute name="mimetype" type="xsd:string" /> + <xsd:attribute ref="xml:space" /> + </xsd:complexType> + </xsd:element> + <xsd:element name="assembly"> + <xsd:complexType> + <xsd:attribute name="alias" type="xsd:string" /> + <xsd:attribute name="name" type="xsd:string" /> + </xsd:complexType> + </xsd:element> + <xsd:element name="data"> + <xsd:complexType> + <xsd:sequence> + <xsd:element name="value" type="xsd:string" minOccurs="0" msdata:Ordinal="1" /> + <xsd:element name="comment" type="xsd:string" minOccurs="0" msdata:Ordinal="2" /> + </xsd:sequence> + <xsd:attribute name="name" type="xsd:string" use="required" msdata:Ordinal="1" /> + <xsd:attribute name="type" type="xsd:string" msdata:Ordinal="3" /> + <xsd:attribute name="mimetype" type="xsd:string" msdata:Ordinal="4" /> + <xsd:attribute ref="xml:space" /> + </xsd:complexType> + </xsd:element> + <xsd:element name="resheader"> + <xsd:complexType> + <xsd:sequence> + <xsd:element name="value" type="xsd:string" minOccurs="0" msdata:Ordinal="1" /> + </xsd:sequence> + <xsd:attribute name="name" type="xsd:string" use="required" /> + </xsd:complexType> + </xsd:element> + </xsd:choice> + </xsd:complexType> + </xsd:element> + </xsd:schema> + <resheader name="resmimetype"> + <value>text/microsoft-resx</value> + </resheader> + <resheader name="version"> + <value>2.0</value> + </resheader> + <resheader name="reader"> + <value>System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value> + </resheader> + <resheader name="writer"> + <value>System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value> + </resheader> + <data name="Cancel" xml:space="preserve"> + <value>Annuller</value> + <comment>ContextActionCell Cancel button</comment> + </data> + <data name="More" xml:space="preserve"> + <value>Mere</value> + <comment>ContextActionCell More button</comment> + </data> +</root>
\ No newline at end of file diff --git a/Xamarin.Forms.Platform.iOS/Resources/StringResources.de.resx b/Xamarin.Forms.Platform.iOS/Resources/StringResources.de.resx new file mode 100644 index 00000000..fa10a5a3 --- /dev/null +++ b/Xamarin.Forms.Platform.iOS/Resources/StringResources.de.resx @@ -0,0 +1,128 @@ +<?xml version="1.0" encoding="utf-8"?> +<root> + <!-- + Microsoft ResX Schema + + Version 2.0 + + The primary goals of this format is to allow a simple XML format + that is mostly human readable. The generation and parsing of the + various data types are done through the TypeConverter classes + associated with the data types. + + Example: + + ... ado.net/XML headers & schema ... + <resheader name="resmimetype">text/microsoft-resx</resheader> + <resheader name="version">2.0</resheader> + <resheader name="reader">System.Resources.ResXResourceReader, System.Windows.Forms, ...</resheader> + <resheader name="writer">System.Resources.ResXResourceWriter, System.Windows.Forms, ...</resheader> + <data name="Name1"><value>this is my long string</value><comment>this is a comment</comment></data> + <data name="Color1" type="System.Drawing.Color, System.Drawing">Blue</data> + <data name="Bitmap1" mimetype="application/x-microsoft.net.object.binary.base64"> + <value>[base64 mime encoded serialized .NET Framework object]</value> + </data> + <data name="Icon1" type="System.Drawing.Icon, System.Drawing" mimetype="application/x-microsoft.net.object.bytearray.base64"> + <value>[base64 mime encoded string representing a byte array form of the .NET Framework object]</value> + <comment>This is a comment</comment> + </data> + + There are any number of "resheader" rows that contain simple + name/value pairs. + + Each data row contains a name, and value. The row also contains a + type or mimetype. Type corresponds to a .NET class that support + text/value conversion through the TypeConverter architecture. + Classes that don't support this are serialized and stored with the + mimetype set. + + The mimetype is used for serialized objects, and tells the + ResXResourceReader how to depersist the object. This is currently not + extensible. For a given mimetype the value must be set accordingly: + + Note - application/x-microsoft.net.object.binary.base64 is the format + that the ResXResourceWriter will generate, however the reader can + read any of the formats listed below. + + mimetype: application/x-microsoft.net.object.binary.base64 + value : The object must be serialized with + : System.Runtime.Serialization.Formatters.Binary.BinaryFormatter + : and then encoded with base64 encoding. + + mimetype: application/x-microsoft.net.object.soap.base64 + value : The object must be serialized with + : System.Runtime.Serialization.Formatters.Soap.SoapFormatter + : and then encoded with base64 encoding. + + mimetype: application/x-microsoft.net.object.bytearray.base64 + value : The object must be serialized into a byte array + : using a System.ComponentModel.TypeConverter + : and then encoded with base64 encoding. + --> + <xsd:schema id="root" xmlns="" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:msdata="urn:schemas-microsoft-com:xml-msdata"> + <xsd:import namespace="http://www.w3.org/XML/1998/namespace" /> + <xsd:element name="root" msdata:IsDataSet="true"> + <xsd:complexType> + <xsd:choice maxOccurs="unbounded"> + <xsd:element name="metadata"> + <xsd:complexType> + <xsd:sequence> + <xsd:element name="value" type="xsd:string" minOccurs="0" /> + </xsd:sequence> + <xsd:attribute name="name" use="required" type="xsd:string" /> + <xsd:attribute name="type" type="xsd:string" /> + <xsd:attribute name="mimetype" type="xsd:string" /> + <xsd:attribute ref="xml:space" /> + </xsd:complexType> + </xsd:element> + <xsd:element name="assembly"> + <xsd:complexType> + <xsd:attribute name="alias" type="xsd:string" /> + <xsd:attribute name="name" type="xsd:string" /> + </xsd:complexType> + </xsd:element> + <xsd:element name="data"> + <xsd:complexType> + <xsd:sequence> + <xsd:element name="value" type="xsd:string" minOccurs="0" msdata:Ordinal="1" /> + <xsd:element name="comment" type="xsd:string" minOccurs="0" msdata:Ordinal="2" /> + </xsd:sequence> + <xsd:attribute name="name" type="xsd:string" use="required" msdata:Ordinal="1" /> + <xsd:attribute name="type" type="xsd:string" msdata:Ordinal="3" /> + <xsd:attribute name="mimetype" type="xsd:string" msdata:Ordinal="4" /> + <xsd:attribute ref="xml:space" /> + </xsd:complexType> + </xsd:element> + <xsd:element name="resheader"> + <xsd:complexType> + <xsd:sequence> + <xsd:element name="value" type="xsd:string" minOccurs="0" msdata:Ordinal="1" /> + </xsd:sequence> + <xsd:attribute name="name" type="xsd:string" use="required" /> + </xsd:complexType> + </xsd:element> + </xsd:choice> + </xsd:complexType> + </xsd:element> + </xsd:schema> + <resheader name="resmimetype"> + <value>text/microsoft-resx</value> + </resheader> + <resheader name="version"> + <value>2.0</value> + </resheader> + <resheader name="reader"> + <value>System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value> + </resheader> + <resheader name="writer"> + <value>System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value> + </resheader> + <data name="Cancel" xml:space="preserve"> + <value>Abbrechen</value> + <comment>ContextActionCell Cancel button</comment> + </data> + <data name="More" xml:space="preserve"> + <value>Mehr</value> + <comment>ContextActionCell More button</comment> + </data> +</root>
\ No newline at end of file diff --git a/Xamarin.Forms.Platform.iOS/Resources/StringResources.el.resx b/Xamarin.Forms.Platform.iOS/Resources/StringResources.el.resx new file mode 100644 index 00000000..d360a038 --- /dev/null +++ b/Xamarin.Forms.Platform.iOS/Resources/StringResources.el.resx @@ -0,0 +1,128 @@ +<?xml version="1.0" encoding="utf-8"?> +<root> + <!-- + Microsoft ResX Schema + + Version 2.0 + + The primary goals of this format is to allow a simple XML format + that is mostly human readable. The generation and parsing of the + various data types are done through the TypeConverter classes + associated with the data types. + + Example: + + ... ado.net/XML headers & schema ... + <resheader name="resmimetype">text/microsoft-resx</resheader> + <resheader name="version">2.0</resheader> + <resheader name="reader">System.Resources.ResXResourceReader, System.Windows.Forms, ...</resheader> + <resheader name="writer">System.Resources.ResXResourceWriter, System.Windows.Forms, ...</resheader> + <data name="Name1"><value>this is my long string</value><comment>this is a comment</comment></data> + <data name="Color1" type="System.Drawing.Color, System.Drawing">Blue</data> + <data name="Bitmap1" mimetype="application/x-microsoft.net.object.binary.base64"> + <value>[base64 mime encoded serialized .NET Framework object]</value> + </data> + <data name="Icon1" type="System.Drawing.Icon, System.Drawing" mimetype="application/x-microsoft.net.object.bytearray.base64"> + <value>[base64 mime encoded string representing a byte array form of the .NET Framework object]</value> + <comment>This is a comment</comment> + </data> + + There are any number of "resheader" rows that contain simple + name/value pairs. + + Each data row contains a name, and value. The row also contains a + type or mimetype. Type corresponds to a .NET class that support + text/value conversion through the TypeConverter architecture. + Classes that don't support this are serialized and stored with the + mimetype set. + + The mimetype is used for serialized objects, and tells the + ResXResourceReader how to depersist the object. This is currently not + extensible. For a given mimetype the value must be set accordingly: + + Note - application/x-microsoft.net.object.binary.base64 is the format + that the ResXResourceWriter will generate, however the reader can + read any of the formats listed below. + + mimetype: application/x-microsoft.net.object.binary.base64 + value : The object must be serialized with + : System.Runtime.Serialization.Formatters.Binary.BinaryFormatter + : and then encoded with base64 encoding. + + mimetype: application/x-microsoft.net.object.soap.base64 + value : The object must be serialized with + : System.Runtime.Serialization.Formatters.Soap.SoapFormatter + : and then encoded with base64 encoding. + + mimetype: application/x-microsoft.net.object.bytearray.base64 + value : The object must be serialized into a byte array + : using a System.ComponentModel.TypeConverter + : and then encoded with base64 encoding. + --> + <xsd:schema id="root" xmlns="" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:msdata="urn:schemas-microsoft-com:xml-msdata"> + <xsd:import namespace="http://www.w3.org/XML/1998/namespace" /> + <xsd:element name="root" msdata:IsDataSet="true"> + <xsd:complexType> + <xsd:choice maxOccurs="unbounded"> + <xsd:element name="metadata"> + <xsd:complexType> + <xsd:sequence> + <xsd:element name="value" type="xsd:string" minOccurs="0" /> + </xsd:sequence> + <xsd:attribute name="name" use="required" type="xsd:string" /> + <xsd:attribute name="type" type="xsd:string" /> + <xsd:attribute name="mimetype" type="xsd:string" /> + <xsd:attribute ref="xml:space" /> + </xsd:complexType> + </xsd:element> + <xsd:element name="assembly"> + <xsd:complexType> + <xsd:attribute name="alias" type="xsd:string" /> + <xsd:attribute name="name" type="xsd:string" /> + </xsd:complexType> + </xsd:element> + <xsd:element name="data"> + <xsd:complexType> + <xsd:sequence> + <xsd:element name="value" type="xsd:string" minOccurs="0" msdata:Ordinal="1" /> + <xsd:element name="comment" type="xsd:string" minOccurs="0" msdata:Ordinal="2" /> + </xsd:sequence> + <xsd:attribute name="name" type="xsd:string" use="required" msdata:Ordinal="1" /> + <xsd:attribute name="type" type="xsd:string" msdata:Ordinal="3" /> + <xsd:attribute name="mimetype" type="xsd:string" msdata:Ordinal="4" /> + <xsd:attribute ref="xml:space" /> + </xsd:complexType> + </xsd:element> + <xsd:element name="resheader"> + <xsd:complexType> + <xsd:sequence> + <xsd:element name="value" type="xsd:string" minOccurs="0" msdata:Ordinal="1" /> + </xsd:sequence> + <xsd:attribute name="name" type="xsd:string" use="required" /> + </xsd:complexType> + </xsd:element> + </xsd:choice> + </xsd:complexType> + </xsd:element> + </xsd:schema> + <resheader name="resmimetype"> + <value>text/microsoft-resx</value> + </resheader> + <resheader name="version"> + <value>2.0</value> + </resheader> + <resheader name="reader"> + <value>System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value> + </resheader> + <resheader name="writer"> + <value>System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value> + </resheader> + <data name="Cancel" xml:space="preserve"> + <value>Ακύρωση</value> + <comment>ContextActionCell Cancel button</comment> + </data> + <data name="More" xml:space="preserve"> + <value>Λοιπά</value> + <comment>ContextActionCell More button</comment> + </data> +</root>
\ No newline at end of file diff --git a/Xamarin.Forms.Platform.iOS/Resources/StringResources.es.resx b/Xamarin.Forms.Platform.iOS/Resources/StringResources.es.resx new file mode 100644 index 00000000..a8bc4fd8 --- /dev/null +++ b/Xamarin.Forms.Platform.iOS/Resources/StringResources.es.resx @@ -0,0 +1,128 @@ +<?xml version="1.0" encoding="utf-8"?> +<root> + <!-- + Microsoft ResX Schema + + Version 2.0 + + The primary goals of this format is to allow a simple XML format + that is mostly human readable. The generation and parsing of the + various data types are done through the TypeConverter classes + associated with the data types. + + Example: + + ... ado.net/XML headers & schema ... + <resheader name="resmimetype">text/microsoft-resx</resheader> + <resheader name="version">2.0</resheader> + <resheader name="reader">System.Resources.ResXResourceReader, System.Windows.Forms, ...</resheader> + <resheader name="writer">System.Resources.ResXResourceWriter, System.Windows.Forms, ...</resheader> + <data name="Name1"><value>this is my long string</value><comment>this is a comment</comment></data> + <data name="Color1" type="System.Drawing.Color, System.Drawing">Blue</data> + <data name="Bitmap1" mimetype="application/x-microsoft.net.object.binary.base64"> + <value>[base64 mime encoded serialized .NET Framework object]</value> + </data> + <data name="Icon1" type="System.Drawing.Icon, System.Drawing" mimetype="application/x-microsoft.net.object.bytearray.base64"> + <value>[base64 mime encoded string representing a byte array form of the .NET Framework object]</value> + <comment>This is a comment</comment> + </data> + + There are any number of "resheader" rows that contain simple + name/value pairs. + + Each data row contains a name, and value. The row also contains a + type or mimetype. Type corresponds to a .NET class that support + text/value conversion through the TypeConverter architecture. + Classes that don't support this are serialized and stored with the + mimetype set. + + The mimetype is used for serialized objects, and tells the + ResXResourceReader how to depersist the object. This is currently not + extensible. For a given mimetype the value must be set accordingly: + + Note - application/x-microsoft.net.object.binary.base64 is the format + that the ResXResourceWriter will generate, however the reader can + read any of the formats listed below. + + mimetype: application/x-microsoft.net.object.binary.base64 + value : The object must be serialized with + : System.Runtime.Serialization.Formatters.Binary.BinaryFormatter + : and then encoded with base64 encoding. + + mimetype: application/x-microsoft.net.object.soap.base64 + value : The object must be serialized with + : System.Runtime.Serialization.Formatters.Soap.SoapFormatter + : and then encoded with base64 encoding. + + mimetype: application/x-microsoft.net.object.bytearray.base64 + value : The object must be serialized into a byte array + : using a System.ComponentModel.TypeConverter + : and then encoded with base64 encoding. + --> + <xsd:schema id="root" xmlns="" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:msdata="urn:schemas-microsoft-com:xml-msdata"> + <xsd:import namespace="http://www.w3.org/XML/1998/namespace" /> + <xsd:element name="root" msdata:IsDataSet="true"> + <xsd:complexType> + <xsd:choice maxOccurs="unbounded"> + <xsd:element name="metadata"> + <xsd:complexType> + <xsd:sequence> + <xsd:element name="value" type="xsd:string" minOccurs="0" /> + </xsd:sequence> + <xsd:attribute name="name" use="required" type="xsd:string" /> + <xsd:attribute name="type" type="xsd:string" /> + <xsd:attribute name="mimetype" type="xsd:string" /> + <xsd:attribute ref="xml:space" /> + </xsd:complexType> + </xsd:element> + <xsd:element name="assembly"> + <xsd:complexType> + <xsd:attribute name="alias" type="xsd:string" /> + <xsd:attribute name="name" type="xsd:string" /> + </xsd:complexType> + </xsd:element> + <xsd:element name="data"> + <xsd:complexType> + <xsd:sequence> + <xsd:element name="value" type="xsd:string" minOccurs="0" msdata:Ordinal="1" /> + <xsd:element name="comment" type="xsd:string" minOccurs="0" msdata:Ordinal="2" /> + </xsd:sequence> + <xsd:attribute name="name" type="xsd:string" use="required" msdata:Ordinal="1" /> + <xsd:attribute name="type" type="xsd:string" msdata:Ordinal="3" /> + <xsd:attribute name="mimetype" type="xsd:string" msdata:Ordinal="4" /> + <xsd:attribute ref="xml:space" /> + </xsd:complexType> + </xsd:element> + <xsd:element name="resheader"> + <xsd:complexType> + <xsd:sequence> + <xsd:element name="value" type="xsd:string" minOccurs="0" msdata:Ordinal="1" /> + </xsd:sequence> + <xsd:attribute name="name" type="xsd:string" use="required" /> + </xsd:complexType> + </xsd:element> + </xsd:choice> + </xsd:complexType> + </xsd:element> + </xsd:schema> + <resheader name="resmimetype"> + <value>text/microsoft-resx</value> + </resheader> + <resheader name="version"> + <value>2.0</value> + </resheader> + <resheader name="reader"> + <value>System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value> + </resheader> + <resheader name="writer"> + <value>System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value> + </resheader> + <data name="Cancel" xml:space="preserve"> + <value>Cancelar</value> + <comment>ContextActionCell Cancel button</comment> + </data> + <data name="More" xml:space="preserve"> + <value>Más</value> + <comment>ContextActionCell More button</comment> + </data> +</root>
\ No newline at end of file diff --git a/Xamarin.Forms.Platform.iOS/Resources/StringResources.fi.resx b/Xamarin.Forms.Platform.iOS/Resources/StringResources.fi.resx new file mode 100644 index 00000000..0244fc71 --- /dev/null +++ b/Xamarin.Forms.Platform.iOS/Resources/StringResources.fi.resx @@ -0,0 +1,128 @@ +<?xml version="1.0" encoding="utf-8"?> +<root> + <!-- + Microsoft ResX Schema + + Version 2.0 + + The primary goals of this format is to allow a simple XML format + that is mostly human readable. The generation and parsing of the + various data types are done through the TypeConverter classes + associated with the data types. + + Example: + + ... ado.net/XML headers & schema ... + <resheader name="resmimetype">text/microsoft-resx</resheader> + <resheader name="version">2.0</resheader> + <resheader name="reader">System.Resources.ResXResourceReader, System.Windows.Forms, ...</resheader> + <resheader name="writer">System.Resources.ResXResourceWriter, System.Windows.Forms, ...</resheader> + <data name="Name1"><value>this is my long string</value><comment>this is a comment</comment></data> + <data name="Color1" type="System.Drawing.Color, System.Drawing">Blue</data> + <data name="Bitmap1" mimetype="application/x-microsoft.net.object.binary.base64"> + <value>[base64 mime encoded serialized .NET Framework object]</value> + </data> + <data name="Icon1" type="System.Drawing.Icon, System.Drawing" mimetype="application/x-microsoft.net.object.bytearray.base64"> + <value>[base64 mime encoded string representing a byte array form of the .NET Framework object]</value> + <comment>This is a comment</comment> + </data> + + There are any number of "resheader" rows that contain simple + name/value pairs. + + Each data row contains a name, and value. The row also contains a + type or mimetype. Type corresponds to a .NET class that support + text/value conversion through the TypeConverter architecture. + Classes that don't support this are serialized and stored with the + mimetype set. + + The mimetype is used for serialized objects, and tells the + ResXResourceReader how to depersist the object. This is currently not + extensible. For a given mimetype the value must be set accordingly: + + Note - application/x-microsoft.net.object.binary.base64 is the format + that the ResXResourceWriter will generate, however the reader can + read any of the formats listed below. + + mimetype: application/x-microsoft.net.object.binary.base64 + value : The object must be serialized with + : System.Runtime.Serialization.Formatters.Binary.BinaryFormatter + : and then encoded with base64 encoding. + + mimetype: application/x-microsoft.net.object.soap.base64 + value : The object must be serialized with + : System.Runtime.Serialization.Formatters.Soap.SoapFormatter + : and then encoded with base64 encoding. + + mimetype: application/x-microsoft.net.object.bytearray.base64 + value : The object must be serialized into a byte array + : using a System.ComponentModel.TypeConverter + : and then encoded with base64 encoding. + --> + <xsd:schema id="root" xmlns="" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:msdata="urn:schemas-microsoft-com:xml-msdata"> + <xsd:import namespace="http://www.w3.org/XML/1998/namespace" /> + <xsd:element name="root" msdata:IsDataSet="true"> + <xsd:complexType> + <xsd:choice maxOccurs="unbounded"> + <xsd:element name="metadata"> + <xsd:complexType> + <xsd:sequence> + <xsd:element name="value" type="xsd:string" minOccurs="0" /> + </xsd:sequence> + <xsd:attribute name="name" use="required" type="xsd:string" /> + <xsd:attribute name="type" type="xsd:string" /> + <xsd:attribute name="mimetype" type="xsd:string" /> + <xsd:attribute ref="xml:space" /> + </xsd:complexType> + </xsd:element> + <xsd:element name="assembly"> + <xsd:complexType> + <xsd:attribute name="alias" type="xsd:string" /> + <xsd:attribute name="name" type="xsd:string" /> + </xsd:complexType> + </xsd:element> + <xsd:element name="data"> + <xsd:complexType> + <xsd:sequence> + <xsd:element name="value" type="xsd:string" minOccurs="0" msdata:Ordinal="1" /> + <xsd:element name="comment" type="xsd:string" minOccurs="0" msdata:Ordinal="2" /> + </xsd:sequence> + <xsd:attribute name="name" type="xsd:string" use="required" msdata:Ordinal="1" /> + <xsd:attribute name="type" type="xsd:string" msdata:Ordinal="3" /> + <xsd:attribute name="mimetype" type="xsd:string" msdata:Ordinal="4" /> + <xsd:attribute ref="xml:space" /> + </xsd:complexType> + </xsd:element> + <xsd:element name="resheader"> + <xsd:complexType> + <xsd:sequence> + <xsd:element name="value" type="xsd:string" minOccurs="0" msdata:Ordinal="1" /> + </xsd:sequence> + <xsd:attribute name="name" type="xsd:string" use="required" /> + </xsd:complexType> + </xsd:element> + </xsd:choice> + </xsd:complexType> + </xsd:element> + </xsd:schema> + <resheader name="resmimetype"> + <value>text/microsoft-resx</value> + </resheader> + <resheader name="version"> + <value>2.0</value> + </resheader> + <resheader name="reader"> + <value>System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value> + </resheader> + <resheader name="writer"> + <value>System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value> + </resheader> + <data name="Cancel" xml:space="preserve"> + <value>Kumoa</value> + <comment>ContextActionCell Cancel button</comment> + </data> + <data name="More" xml:space="preserve"> + <value>Muut</value> + <comment>ContextActionCell More button</comment> + </data> +</root>
\ No newline at end of file diff --git a/Xamarin.Forms.Platform.iOS/Resources/StringResources.fr.resx b/Xamarin.Forms.Platform.iOS/Resources/StringResources.fr.resx new file mode 100644 index 00000000..819845a0 --- /dev/null +++ b/Xamarin.Forms.Platform.iOS/Resources/StringResources.fr.resx @@ -0,0 +1,128 @@ +<?xml version="1.0" encoding="utf-8"?> +<root> + <!-- + Microsoft ResX Schema + + Version 2.0 + + The primary goals of this format is to allow a simple XML format + that is mostly human readable. The generation and parsing of the + various data types are done through the TypeConverter classes + associated with the data types. + + Example: + + ... ado.net/XML headers & schema ... + <resheader name="resmimetype">text/microsoft-resx</resheader> + <resheader name="version">2.0</resheader> + <resheader name="reader">System.Resources.ResXResourceReader, System.Windows.Forms, ...</resheader> + <resheader name="writer">System.Resources.ResXResourceWriter, System.Windows.Forms, ...</resheader> + <data name="Name1"><value>this is my long string</value><comment>this is a comment</comment></data> + <data name="Color1" type="System.Drawing.Color, System.Drawing">Blue</data> + <data name="Bitmap1" mimetype="application/x-microsoft.net.object.binary.base64"> + <value>[base64 mime encoded serialized .NET Framework object]</value> + </data> + <data name="Icon1" type="System.Drawing.Icon, System.Drawing" mimetype="application/x-microsoft.net.object.bytearray.base64"> + <value>[base64 mime encoded string representing a byte array form of the .NET Framework object]</value> + <comment>This is a comment</comment> + </data> + + There are any number of "resheader" rows that contain simple + name/value pairs. + + Each data row contains a name, and value. The row also contains a + type or mimetype. Type corresponds to a .NET class that support + text/value conversion through the TypeConverter architecture. + Classes that don't support this are serialized and stored with the + mimetype set. + + The mimetype is used for serialized objects, and tells the + ResXResourceReader how to depersist the object. This is currently not + extensible. For a given mimetype the value must be set accordingly: + + Note - application/x-microsoft.net.object.binary.base64 is the format + that the ResXResourceWriter will generate, however the reader can + read any of the formats listed below. + + mimetype: application/x-microsoft.net.object.binary.base64 + value : The object must be serialized with + : System.Runtime.Serialization.Formatters.Binary.BinaryFormatter + : and then encoded with base64 encoding. + + mimetype: application/x-microsoft.net.object.soap.base64 + value : The object must be serialized with + : System.Runtime.Serialization.Formatters.Soap.SoapFormatter + : and then encoded with base64 encoding. + + mimetype: application/x-microsoft.net.object.bytearray.base64 + value : The object must be serialized into a byte array + : using a System.ComponentModel.TypeConverter + : and then encoded with base64 encoding. + --> + <xsd:schema id="root" xmlns="" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:msdata="urn:schemas-microsoft-com:xml-msdata"> + <xsd:import namespace="http://www.w3.org/XML/1998/namespace" /> + <xsd:element name="root" msdata:IsDataSet="true"> + <xsd:complexType> + <xsd:choice maxOccurs="unbounded"> + <xsd:element name="metadata"> + <xsd:complexType> + <xsd:sequence> + <xsd:element name="value" type="xsd:string" minOccurs="0" /> + </xsd:sequence> + <xsd:attribute name="name" use="required" type="xsd:string" /> + <xsd:attribute name="type" type="xsd:string" /> + <xsd:attribute name="mimetype" type="xsd:string" /> + <xsd:attribute ref="xml:space" /> + </xsd:complexType> + </xsd:element> + <xsd:element name="assembly"> + <xsd:complexType> + <xsd:attribute name="alias" type="xsd:string" /> + <xsd:attribute name="name" type="xsd:string" /> + </xsd:complexType> + </xsd:element> + <xsd:element name="data"> + <xsd:complexType> + <xsd:sequence> + <xsd:element name="value" type="xsd:string" minOccurs="0" msdata:Ordinal="1" /> + <xsd:element name="comment" type="xsd:string" minOccurs="0" msdata:Ordinal="2" /> + </xsd:sequence> + <xsd:attribute name="name" type="xsd:string" use="required" msdata:Ordinal="1" /> + <xsd:attribute name="type" type="xsd:string" msdata:Ordinal="3" /> + <xsd:attribute name="mimetype" type="xsd:string" msdata:Ordinal="4" /> + <xsd:attribute ref="xml:space" /> + </xsd:complexType> + </xsd:element> + <xsd:element name="resheader"> + <xsd:complexType> + <xsd:sequence> + <xsd:element name="value" type="xsd:string" minOccurs="0" msdata:Ordinal="1" /> + </xsd:sequence> + <xsd:attribute name="name" type="xsd:string" use="required" /> + </xsd:complexType> + </xsd:element> + </xsd:choice> + </xsd:complexType> + </xsd:element> + </xsd:schema> + <resheader name="resmimetype"> + <value>text/microsoft-resx</value> + </resheader> + <resheader name="version"> + <value>2.0</value> + </resheader> + <resheader name="reader"> + <value>System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value> + </resheader> + <resheader name="writer"> + <value>System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value> + </resheader> + <data name="Cancel" xml:space="preserve"> + <value>Annuler</value> + <comment>ContextActionCell Cancel button</comment> + </data> + <data name="More" xml:space="preserve"> + <value>Options</value> + <comment>ContextActionCell More button</comment> + </data> +</root>
\ No newline at end of file diff --git a/Xamarin.Forms.Platform.iOS/Resources/StringResources.he.resx b/Xamarin.Forms.Platform.iOS/Resources/StringResources.he.resx new file mode 100644 index 00000000..fe8724a3 --- /dev/null +++ b/Xamarin.Forms.Platform.iOS/Resources/StringResources.he.resx @@ -0,0 +1,128 @@ +<?xml version="1.0" encoding="utf-8"?> +<root> + <!-- + Microsoft ResX Schema + + Version 2.0 + + The primary goals of this format is to allow a simple XML format + that is mostly human readable. The generation and parsing of the + various data types are done through the TypeConverter classes + associated with the data types. + + Example: + + ... ado.net/XML headers & schema ... + <resheader name="resmimetype">text/microsoft-resx</resheader> + <resheader name="version">2.0</resheader> + <resheader name="reader">System.Resources.ResXResourceReader, System.Windows.Forms, ...</resheader> + <resheader name="writer">System.Resources.ResXResourceWriter, System.Windows.Forms, ...</resheader> + <data name="Name1"><value>this is my long string</value><comment>this is a comment</comment></data> + <data name="Color1" type="System.Drawing.Color, System.Drawing">Blue</data> + <data name="Bitmap1" mimetype="application/x-microsoft.net.object.binary.base64"> + <value>[base64 mime encoded serialized .NET Framework object]</value> + </data> + <data name="Icon1" type="System.Drawing.Icon, System.Drawing" mimetype="application/x-microsoft.net.object.bytearray.base64"> + <value>[base64 mime encoded string representing a byte array form of the .NET Framework object]</value> + <comment>This is a comment</comment> + </data> + + There are any number of "resheader" rows that contain simple + name/value pairs. + + Each data row contains a name, and value. The row also contains a + type or mimetype. Type corresponds to a .NET class that support + text/value conversion through the TypeConverter architecture. + Classes that don't support this are serialized and stored with the + mimetype set. + + The mimetype is used for serialized objects, and tells the + ResXResourceReader how to depersist the object. This is currently not + extensible. For a given mimetype the value must be set accordingly: + + Note - application/x-microsoft.net.object.binary.base64 is the format + that the ResXResourceWriter will generate, however the reader can + read any of the formats listed below. + + mimetype: application/x-microsoft.net.object.binary.base64 + value : The object must be serialized with + : System.Runtime.Serialization.Formatters.Binary.BinaryFormatter + : and then encoded with base64 encoding. + + mimetype: application/x-microsoft.net.object.soap.base64 + value : The object must be serialized with + : System.Runtime.Serialization.Formatters.Soap.SoapFormatter + : and then encoded with base64 encoding. + + mimetype: application/x-microsoft.net.object.bytearray.base64 + value : The object must be serialized into a byte array + : using a System.ComponentModel.TypeConverter + : and then encoded with base64 encoding. + --> + <xsd:schema id="root" xmlns="" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:msdata="urn:schemas-microsoft-com:xml-msdata"> + <xsd:import namespace="http://www.w3.org/XML/1998/namespace" /> + <xsd:element name="root" msdata:IsDataSet="true"> + <xsd:complexType> + <xsd:choice maxOccurs="unbounded"> + <xsd:element name="metadata"> + <xsd:complexType> + <xsd:sequence> + <xsd:element name="value" type="xsd:string" minOccurs="0" /> + </xsd:sequence> + <xsd:attribute name="name" use="required" type="xsd:string" /> + <xsd:attribute name="type" type="xsd:string" /> + <xsd:attribute name="mimetype" type="xsd:string" /> + <xsd:attribute ref="xml:space" /> + </xsd:complexType> + </xsd:element> + <xsd:element name="assembly"> + <xsd:complexType> + <xsd:attribute name="alias" type="xsd:string" /> + <xsd:attribute name="name" type="xsd:string" /> + </xsd:complexType> + </xsd:element> + <xsd:element name="data"> + <xsd:complexType> + <xsd:sequence> + <xsd:element name="value" type="xsd:string" minOccurs="0" msdata:Ordinal="1" /> + <xsd:element name="comment" type="xsd:string" minOccurs="0" msdata:Ordinal="2" /> + </xsd:sequence> + <xsd:attribute name="name" type="xsd:string" use="required" msdata:Ordinal="1" /> + <xsd:attribute name="type" type="xsd:string" msdata:Ordinal="3" /> + <xsd:attribute name="mimetype" type="xsd:string" msdata:Ordinal="4" /> + <xsd:attribute ref="xml:space" /> + </xsd:complexType> + </xsd:element> + <xsd:element name="resheader"> + <xsd:complexType> + <xsd:sequence> + <xsd:element name="value" type="xsd:string" minOccurs="0" msdata:Ordinal="1" /> + </xsd:sequence> + <xsd:attribute name="name" type="xsd:string" use="required" /> + </xsd:complexType> + </xsd:element> + </xsd:choice> + </xsd:complexType> + </xsd:element> + </xsd:schema> + <resheader name="resmimetype"> + <value>text/microsoft-resx</value> + </resheader> + <resheader name="version"> + <value>2.0</value> + </resheader> + <resheader name="reader"> + <value>System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value> + </resheader> + <resheader name="writer"> + <value>System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value> + </resheader> + <data name="Cancel" xml:space="preserve"> + <value>ביטול</value> + <comment>ContextActionCell Cancel button</comment> + </data> + <data name="More" xml:space="preserve"> + <value>עוד</value> + <comment>ContextActionCell More button</comment> + </data> +</root>
\ No newline at end of file diff --git a/Xamarin.Forms.Platform.iOS/Resources/StringResources.hi.resx b/Xamarin.Forms.Platform.iOS/Resources/StringResources.hi.resx new file mode 100644 index 00000000..5ad57b4c --- /dev/null +++ b/Xamarin.Forms.Platform.iOS/Resources/StringResources.hi.resx @@ -0,0 +1,128 @@ +<?xml version="1.0" encoding="utf-8"?> +<root> + <!-- + Microsoft ResX Schema + + Version 2.0 + + The primary goals of this format is to allow a simple XML format + that is mostly human readable. The generation and parsing of the + various data types are done through the TypeConverter classes + associated with the data types. + + Example: + + ... ado.net/XML headers & schema ... + <resheader name="resmimetype">text/microsoft-resx</resheader> + <resheader name="version">2.0</resheader> + <resheader name="reader">System.Resources.ResXResourceReader, System.Windows.Forms, ...</resheader> + <resheader name="writer">System.Resources.ResXResourceWriter, System.Windows.Forms, ...</resheader> + <data name="Name1"><value>this is my long string</value><comment>this is a comment</comment></data> + <data name="Color1" type="System.Drawing.Color, System.Drawing">Blue</data> + <data name="Bitmap1" mimetype="application/x-microsoft.net.object.binary.base64"> + <value>[base64 mime encoded serialized .NET Framework object]</value> + </data> + <data name="Icon1" type="System.Drawing.Icon, System.Drawing" mimetype="application/x-microsoft.net.object.bytearray.base64"> + <value>[base64 mime encoded string representing a byte array form of the .NET Framework object]</value> + <comment>This is a comment</comment> + </data> + + There are any number of "resheader" rows that contain simple + name/value pairs. + + Each data row contains a name, and value. The row also contains a + type or mimetype. Type corresponds to a .NET class that support + text/value conversion through the TypeConverter architecture. + Classes that don't support this are serialized and stored with the + mimetype set. + + The mimetype is used for serialized objects, and tells the + ResXResourceReader how to depersist the object. This is currently not + extensible. For a given mimetype the value must be set accordingly: + + Note - application/x-microsoft.net.object.binary.base64 is the format + that the ResXResourceWriter will generate, however the reader can + read any of the formats listed below. + + mimetype: application/x-microsoft.net.object.binary.base64 + value : The object must be serialized with + : System.Runtime.Serialization.Formatters.Binary.BinaryFormatter + : and then encoded with base64 encoding. + + mimetype: application/x-microsoft.net.object.soap.base64 + value : The object must be serialized with + : System.Runtime.Serialization.Formatters.Soap.SoapFormatter + : and then encoded with base64 encoding. + + mimetype: application/x-microsoft.net.object.bytearray.base64 + value : The object must be serialized into a byte array + : using a System.ComponentModel.TypeConverter + : and then encoded with base64 encoding. + --> + <xsd:schema id="root" xmlns="" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:msdata="urn:schemas-microsoft-com:xml-msdata"> + <xsd:import namespace="http://www.w3.org/XML/1998/namespace" /> + <xsd:element name="root" msdata:IsDataSet="true"> + <xsd:complexType> + <xsd:choice maxOccurs="unbounded"> + <xsd:element name="metadata"> + <xsd:complexType> + <xsd:sequence> + <xsd:element name="value" type="xsd:string" minOccurs="0" /> + </xsd:sequence> + <xsd:attribute name="name" use="required" type="xsd:string" /> + <xsd:attribute name="type" type="xsd:string" /> + <xsd:attribute name="mimetype" type="xsd:string" /> + <xsd:attribute ref="xml:space" /> + </xsd:complexType> + </xsd:element> + <xsd:element name="assembly"> + <xsd:complexType> + <xsd:attribute name="alias" type="xsd:string" /> + <xsd:attribute name="name" type="xsd:string" /> + </xsd:complexType> + </xsd:element> + <xsd:element name="data"> + <xsd:complexType> + <xsd:sequence> + <xsd:element name="value" type="xsd:string" minOccurs="0" msdata:Ordinal="1" /> + <xsd:element name="comment" type="xsd:string" minOccurs="0" msdata:Ordinal="2" /> + </xsd:sequence> + <xsd:attribute name="name" type="xsd:string" use="required" msdata:Ordinal="1" /> + <xsd:attribute name="type" type="xsd:string" msdata:Ordinal="3" /> + <xsd:attribute name="mimetype" type="xsd:string" msdata:Ordinal="4" /> + <xsd:attribute ref="xml:space" /> + </xsd:complexType> + </xsd:element> + <xsd:element name="resheader"> + <xsd:complexType> + <xsd:sequence> + <xsd:element name="value" type="xsd:string" minOccurs="0" msdata:Ordinal="1" /> + </xsd:sequence> + <xsd:attribute name="name" type="xsd:string" use="required" /> + </xsd:complexType> + </xsd:element> + </xsd:choice> + </xsd:complexType> + </xsd:element> + </xsd:schema> + <resheader name="resmimetype"> + <value>text/microsoft-resx</value> + </resheader> + <resheader name="version"> + <value>2.0</value> + </resheader> + <resheader name="reader"> + <value>System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value> + </resheader> + <resheader name="writer"> + <value>System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value> + </resheader> + <data name="Cancel" xml:space="preserve"> + <value>रद्द करें</value> + <comment>ContextActionCell Cancel button</comment> + </data> + <data name="More" xml:space="preserve"> + <value>अधिक</value> + <comment>ContextActionCell More button</comment> + </data> +</root>
\ No newline at end of file diff --git a/Xamarin.Forms.Platform.iOS/Resources/StringResources.hr.resx b/Xamarin.Forms.Platform.iOS/Resources/StringResources.hr.resx new file mode 100644 index 00000000..c7e01f21 --- /dev/null +++ b/Xamarin.Forms.Platform.iOS/Resources/StringResources.hr.resx @@ -0,0 +1,128 @@ +<?xml version="1.0" encoding="utf-8"?> +<root> + <!-- + Microsoft ResX Schema + + Version 2.0 + + The primary goals of this format is to allow a simple XML format + that is mostly human readable. The generation and parsing of the + various data types are done through the TypeConverter classes + associated with the data types. + + Example: + + ... ado.net/XML headers & schema ... + <resheader name="resmimetype">text/microsoft-resx</resheader> + <resheader name="version">2.0</resheader> + <resheader name="reader">System.Resources.ResXResourceReader, System.Windows.Forms, ...</resheader> + <resheader name="writer">System.Resources.ResXResourceWriter, System.Windows.Forms, ...</resheader> + <data name="Name1"><value>this is my long string</value><comment>this is a comment</comment></data> + <data name="Color1" type="System.Drawing.Color, System.Drawing">Blue</data> + <data name="Bitmap1" mimetype="application/x-microsoft.net.object.binary.base64"> + <value>[base64 mime encoded serialized .NET Framework object]</value> + </data> + <data name="Icon1" type="System.Drawing.Icon, System.Drawing" mimetype="application/x-microsoft.net.object.bytearray.base64"> + <value>[base64 mime encoded string representing a byte array form of the .NET Framework object]</value> + <comment>This is a comment</comment> + </data> + + There are any number of "resheader" rows that contain simple + name/value pairs. + + Each data row contains a name, and value. The row also contains a + type or mimetype. Type corresponds to a .NET class that support + text/value conversion through the TypeConverter architecture. + Classes that don't support this are serialized and stored with the + mimetype set. + + The mimetype is used for serialized objects, and tells the + ResXResourceReader how to depersist the object. This is currently not + extensible. For a given mimetype the value must be set accordingly: + + Note - application/x-microsoft.net.object.binary.base64 is the format + that the ResXResourceWriter will generate, however the reader can + read any of the formats listed below. + + mimetype: application/x-microsoft.net.object.binary.base64 + value : The object must be serialized with + : System.Runtime.Serialization.Formatters.Binary.BinaryFormatter + : and then encoded with base64 encoding. + + mimetype: application/x-microsoft.net.object.soap.base64 + value : The object must be serialized with + : System.Runtime.Serialization.Formatters.Soap.SoapFormatter + : and then encoded with base64 encoding. + + mimetype: application/x-microsoft.net.object.bytearray.base64 + value : The object must be serialized into a byte array + : using a System.ComponentModel.TypeConverter + : and then encoded with base64 encoding. + --> + <xsd:schema id="root" xmlns="" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:msdata="urn:schemas-microsoft-com:xml-msdata"> + <xsd:import namespace="http://www.w3.org/XML/1998/namespace" /> + <xsd:element name="root" msdata:IsDataSet="true"> + <xsd:complexType> + <xsd:choice maxOccurs="unbounded"> + <xsd:element name="metadata"> + <xsd:complexType> + <xsd:sequence> + <xsd:element name="value" type="xsd:string" minOccurs="0" /> + </xsd:sequence> + <xsd:attribute name="name" use="required" type="xsd:string" /> + <xsd:attribute name="type" type="xsd:string" /> + <xsd:attribute name="mimetype" type="xsd:string" /> + <xsd:attribute ref="xml:space" /> + </xsd:complexType> + </xsd:element> + <xsd:element name="assembly"> + <xsd:complexType> + <xsd:attribute name="alias" type="xsd:string" /> + <xsd:attribute name="name" type="xsd:string" /> + </xsd:complexType> + </xsd:element> + <xsd:element name="data"> + <xsd:complexType> + <xsd:sequence> + <xsd:element name="value" type="xsd:string" minOccurs="0" msdata:Ordinal="1" /> + <xsd:element name="comment" type="xsd:string" minOccurs="0" msdata:Ordinal="2" /> + </xsd:sequence> + <xsd:attribute name="name" type="xsd:string" use="required" msdata:Ordinal="1" /> + <xsd:attribute name="type" type="xsd:string" msdata:Ordinal="3" /> + <xsd:attribute name="mimetype" type="xsd:string" msdata:Ordinal="4" /> + <xsd:attribute ref="xml:space" /> + </xsd:complexType> + </xsd:element> + <xsd:element name="resheader"> + <xsd:complexType> + <xsd:sequence> + <xsd:element name="value" type="xsd:string" minOccurs="0" msdata:Ordinal="1" /> + </xsd:sequence> + <xsd:attribute name="name" type="xsd:string" use="required" /> + </xsd:complexType> + </xsd:element> + </xsd:choice> + </xsd:complexType> + </xsd:element> + </xsd:schema> + <resheader name="resmimetype"> + <value>text/microsoft-resx</value> + </resheader> + <resheader name="version"> + <value>2.0</value> + </resheader> + <resheader name="reader"> + <value>System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value> + </resheader> + <resheader name="writer"> + <value>System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value> + </resheader> + <data name="Cancel" xml:space="preserve"> + <value>Poništi</value> + <comment>ContextActionCell Cancel button</comment> + </data> + <data name="More" xml:space="preserve"> + <value>Više</value> + <comment>ContextActionCell More button</comment> + </data> +</root>
\ No newline at end of file diff --git a/Xamarin.Forms.Platform.iOS/Resources/StringResources.hu.resx b/Xamarin.Forms.Platform.iOS/Resources/StringResources.hu.resx new file mode 100644 index 00000000..1942899b --- /dev/null +++ b/Xamarin.Forms.Platform.iOS/Resources/StringResources.hu.resx @@ -0,0 +1,128 @@ +<?xml version="1.0" encoding="utf-8"?> +<root> + <!-- + Microsoft ResX Schema + + Version 2.0 + + The primary goals of this format is to allow a simple XML format + that is mostly human readable. The generation and parsing of the + various data types are done through the TypeConverter classes + associated with the data types. + + Example: + + ... ado.net/XML headers & schema ... + <resheader name="resmimetype">text/microsoft-resx</resheader> + <resheader name="version">2.0</resheader> + <resheader name="reader">System.Resources.ResXResourceReader, System.Windows.Forms, ...</resheader> + <resheader name="writer">System.Resources.ResXResourceWriter, System.Windows.Forms, ...</resheader> + <data name="Name1"><value>this is my long string</value><comment>this is a comment</comment></data> + <data name="Color1" type="System.Drawing.Color, System.Drawing">Blue</data> + <data name="Bitmap1" mimetype="application/x-microsoft.net.object.binary.base64"> + <value>[base64 mime encoded serialized .NET Framework object]</value> + </data> + <data name="Icon1" type="System.Drawing.Icon, System.Drawing" mimetype="application/x-microsoft.net.object.bytearray.base64"> + <value>[base64 mime encoded string representing a byte array form of the .NET Framework object]</value> + <comment>This is a comment</comment> + </data> + + There are any number of "resheader" rows that contain simple + name/value pairs. + + Each data row contains a name, and value. The row also contains a + type or mimetype. Type corresponds to a .NET class that support + text/value conversion through the TypeConverter architecture. + Classes that don't support this are serialized and stored with the + mimetype set. + + The mimetype is used for serialized objects, and tells the + ResXResourceReader how to depersist the object. This is currently not + extensible. For a given mimetype the value must be set accordingly: + + Note - application/x-microsoft.net.object.binary.base64 is the format + that the ResXResourceWriter will generate, however the reader can + read any of the formats listed below. + + mimetype: application/x-microsoft.net.object.binary.base64 + value : The object must be serialized with + : System.Runtime.Serialization.Formatters.Binary.BinaryFormatter + : and then encoded with base64 encoding. + + mimetype: application/x-microsoft.net.object.soap.base64 + value : The object must be serialized with + : System.Runtime.Serialization.Formatters.Soap.SoapFormatter + : and then encoded with base64 encoding. + + mimetype: application/x-microsoft.net.object.bytearray.base64 + value : The object must be serialized into a byte array + : using a System.ComponentModel.TypeConverter + : and then encoded with base64 encoding. + --> + <xsd:schema id="root" xmlns="" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:msdata="urn:schemas-microsoft-com:xml-msdata"> + <xsd:import namespace="http://www.w3.org/XML/1998/namespace" /> + <xsd:element name="root" msdata:IsDataSet="true"> + <xsd:complexType> + <xsd:choice maxOccurs="unbounded"> + <xsd:element name="metadata"> + <xsd:complexType> + <xsd:sequence> + <xsd:element name="value" type="xsd:string" minOccurs="0" /> + </xsd:sequence> + <xsd:attribute name="name" use="required" type="xsd:string" /> + <xsd:attribute name="type" type="xsd:string" /> + <xsd:attribute name="mimetype" type="xsd:string" /> + <xsd:attribute ref="xml:space" /> + </xsd:complexType> + </xsd:element> + <xsd:element name="assembly"> + <xsd:complexType> + <xsd:attribute name="alias" type="xsd:string" /> + <xsd:attribute name="name" type="xsd:string" /> + </xsd:complexType> + </xsd:element> + <xsd:element name="data"> + <xsd:complexType> + <xsd:sequence> + <xsd:element name="value" type="xsd:string" minOccurs="0" msdata:Ordinal="1" /> + <xsd:element name="comment" type="xsd:string" minOccurs="0" msdata:Ordinal="2" /> + </xsd:sequence> + <xsd:attribute name="name" type="xsd:string" use="required" msdata:Ordinal="1" /> + <xsd:attribute name="type" type="xsd:string" msdata:Ordinal="3" /> + <xsd:attribute name="mimetype" type="xsd:string" msdata:Ordinal="4" /> + <xsd:attribute ref="xml:space" /> + </xsd:complexType> + </xsd:element> + <xsd:element name="resheader"> + <xsd:complexType> + <xsd:sequence> + <xsd:element name="value" type="xsd:string" minOccurs="0" msdata:Ordinal="1" /> + </xsd:sequence> + <xsd:attribute name="name" type="xsd:string" use="required" /> + </xsd:complexType> + </xsd:element> + </xsd:choice> + </xsd:complexType> + </xsd:element> + </xsd:schema> + <resheader name="resmimetype"> + <value>text/microsoft-resx</value> + </resheader> + <resheader name="version"> + <value>2.0</value> + </resheader> + <resheader name="reader"> + <value>System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value> + </resheader> + <resheader name="writer"> + <value>System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value> + </resheader> + <data name="Cancel" xml:space="preserve"> + <value>Mégsem</value> + <comment>ContextActionCell Cancel button</comment> + </data> + <data name="More" xml:space="preserve"> + <value>Továbbiak</value> + <comment>ContextActionCell More button</comment> + </data> +</root>
\ No newline at end of file diff --git a/Xamarin.Forms.Platform.iOS/Resources/StringResources.id.resx b/Xamarin.Forms.Platform.iOS/Resources/StringResources.id.resx new file mode 100644 index 00000000..182b8e94 --- /dev/null +++ b/Xamarin.Forms.Platform.iOS/Resources/StringResources.id.resx @@ -0,0 +1,128 @@ +<?xml version="1.0" encoding="utf-8"?> +<root> + <!-- + Microsoft ResX Schema + + Version 2.0 + + The primary goals of this format is to allow a simple XML format + that is mostly human readable. The generation and parsing of the + various data types are done through the TypeConverter classes + associated with the data types. + + Example: + + ... ado.net/XML headers & schema ... + <resheader name="resmimetype">text/microsoft-resx</resheader> + <resheader name="version">2.0</resheader> + <resheader name="reader">System.Resources.ResXResourceReader, System.Windows.Forms, ...</resheader> + <resheader name="writer">System.Resources.ResXResourceWriter, System.Windows.Forms, ...</resheader> + <data name="Name1"><value>this is my long string</value><comment>this is a comment</comment></data> + <data name="Color1" type="System.Drawing.Color, System.Drawing">Blue</data> + <data name="Bitmap1" mimetype="application/x-microsoft.net.object.binary.base64"> + <value>[base64 mime encoded serialized .NET Framework object]</value> + </data> + <data name="Icon1" type="System.Drawing.Icon, System.Drawing" mimetype="application/x-microsoft.net.object.bytearray.base64"> + <value>[base64 mime encoded string representing a byte array form of the .NET Framework object]</value> + <comment>This is a comment</comment> + </data> + + There are any number of "resheader" rows that contain simple + name/value pairs. + + Each data row contains a name, and value. The row also contains a + type or mimetype. Type corresponds to a .NET class that support + text/value conversion through the TypeConverter architecture. + Classes that don't support this are serialized and stored with the + mimetype set. + + The mimetype is used for serialized objects, and tells the + ResXResourceReader how to depersist the object. This is currently not + extensible. For a given mimetype the value must be set accordingly: + + Note - application/x-microsoft.net.object.binary.base64 is the format + that the ResXResourceWriter will generate, however the reader can + read any of the formats listed below. + + mimetype: application/x-microsoft.net.object.binary.base64 + value : The object must be serialized with + : System.Runtime.Serialization.Formatters.Binary.BinaryFormatter + : and then encoded with base64 encoding. + + mimetype: application/x-microsoft.net.object.soap.base64 + value : The object must be serialized with + : System.Runtime.Serialization.Formatters.Soap.SoapFormatter + : and then encoded with base64 encoding. + + mimetype: application/x-microsoft.net.object.bytearray.base64 + value : The object must be serialized into a byte array + : using a System.ComponentModel.TypeConverter + : and then encoded with base64 encoding. + --> + <xsd:schema id="root" xmlns="" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:msdata="urn:schemas-microsoft-com:xml-msdata"> + <xsd:import namespace="http://www.w3.org/XML/1998/namespace" /> + <xsd:element name="root" msdata:IsDataSet="true"> + <xsd:complexType> + <xsd:choice maxOccurs="unbounded"> + <xsd:element name="metadata"> + <xsd:complexType> + <xsd:sequence> + <xsd:element name="value" type="xsd:string" minOccurs="0" /> + </xsd:sequence> + <xsd:attribute name="name" use="required" type="xsd:string" /> + <xsd:attribute name="type" type="xsd:string" /> + <xsd:attribute name="mimetype" type="xsd:string" /> + <xsd:attribute ref="xml:space" /> + </xsd:complexType> + </xsd:element> + <xsd:element name="assembly"> + <xsd:complexType> + <xsd:attribute name="alias" type="xsd:string" /> + <xsd:attribute name="name" type="xsd:string" /> + </xsd:complexType> + </xsd:element> + <xsd:element name="data"> + <xsd:complexType> + <xsd:sequence> + <xsd:element name="value" type="xsd:string" minOccurs="0" msdata:Ordinal="1" /> + <xsd:element name="comment" type="xsd:string" minOccurs="0" msdata:Ordinal="2" /> + </xsd:sequence> + <xsd:attribute name="name" type="xsd:string" use="required" msdata:Ordinal="1" /> + <xsd:attribute name="type" type="xsd:string" msdata:Ordinal="3" /> + <xsd:attribute name="mimetype" type="xsd:string" msdata:Ordinal="4" /> + <xsd:attribute ref="xml:space" /> + </xsd:complexType> + </xsd:element> + <xsd:element name="resheader"> + <xsd:complexType> + <xsd:sequence> + <xsd:element name="value" type="xsd:string" minOccurs="0" msdata:Ordinal="1" /> + </xsd:sequence> + <xsd:attribute name="name" type="xsd:string" use="required" /> + </xsd:complexType> + </xsd:element> + </xsd:choice> + </xsd:complexType> + </xsd:element> + </xsd:schema> + <resheader name="resmimetype"> + <value>text/microsoft-resx</value> + </resheader> + <resheader name="version"> + <value>2.0</value> + </resheader> + <resheader name="reader"> + <value>System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value> + </resheader> + <resheader name="writer"> + <value>System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value> + </resheader> + <data name="Cancel" xml:space="preserve"> + <value>Batalkan</value> + <comment>ContextActionCell Cancel button</comment> + </data> + <data name="More" xml:space="preserve"> + <value>Lainnya</value> + <comment>ContextActionCell More button</comment> + </data> +</root>
\ No newline at end of file diff --git a/Xamarin.Forms.Platform.iOS/Resources/StringResources.it.resx b/Xamarin.Forms.Platform.iOS/Resources/StringResources.it.resx new file mode 100644 index 00000000..e7b49e09 --- /dev/null +++ b/Xamarin.Forms.Platform.iOS/Resources/StringResources.it.resx @@ -0,0 +1,128 @@ +<?xml version="1.0" encoding="utf-8"?> +<root> + <!-- + Microsoft ResX Schema + + Version 2.0 + + The primary goals of this format is to allow a simple XML format + that is mostly human readable. The generation and parsing of the + various data types are done through the TypeConverter classes + associated with the data types. + + Example: + + ... ado.net/XML headers & schema ... + <resheader name="resmimetype">text/microsoft-resx</resheader> + <resheader name="version">2.0</resheader> + <resheader name="reader">System.Resources.ResXResourceReader, System.Windows.Forms, ...</resheader> + <resheader name="writer">System.Resources.ResXResourceWriter, System.Windows.Forms, ...</resheader> + <data name="Name1"><value>this is my long string</value><comment>this is a comment</comment></data> + <data name="Color1" type="System.Drawing.Color, System.Drawing">Blue</data> + <data name="Bitmap1" mimetype="application/x-microsoft.net.object.binary.base64"> + <value>[base64 mime encoded serialized .NET Framework object]</value> + </data> + <data name="Icon1" type="System.Drawing.Icon, System.Drawing" mimetype="application/x-microsoft.net.object.bytearray.base64"> + <value>[base64 mime encoded string representing a byte array form of the .NET Framework object]</value> + <comment>This is a comment</comment> + </data> + + There are any number of "resheader" rows that contain simple + name/value pairs. + + Each data row contains a name, and value. The row also contains a + type or mimetype. Type corresponds to a .NET class that support + text/value conversion through the TypeConverter architecture. + Classes that don't support this are serialized and stored with the + mimetype set. + + The mimetype is used for serialized objects, and tells the + ResXResourceReader how to depersist the object. This is currently not + extensible. For a given mimetype the value must be set accordingly: + + Note - application/x-microsoft.net.object.binary.base64 is the format + that the ResXResourceWriter will generate, however the reader can + read any of the formats listed below. + + mimetype: application/x-microsoft.net.object.binary.base64 + value : The object must be serialized with + : System.Runtime.Serialization.Formatters.Binary.BinaryFormatter + : and then encoded with base64 encoding. + + mimetype: application/x-microsoft.net.object.soap.base64 + value : The object must be serialized with + : System.Runtime.Serialization.Formatters.Soap.SoapFormatter + : and then encoded with base64 encoding. + + mimetype: application/x-microsoft.net.object.bytearray.base64 + value : The object must be serialized into a byte array + : using a System.ComponentModel.TypeConverter + : and then encoded with base64 encoding. + --> + <xsd:schema id="root" xmlns="" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:msdata="urn:schemas-microsoft-com:xml-msdata"> + <xsd:import namespace="http://www.w3.org/XML/1998/namespace" /> + <xsd:element name="root" msdata:IsDataSet="true"> + <xsd:complexType> + <xsd:choice maxOccurs="unbounded"> + <xsd:element name="metadata"> + <xsd:complexType> + <xsd:sequence> + <xsd:element name="value" type="xsd:string" minOccurs="0" /> + </xsd:sequence> + <xsd:attribute name="name" use="required" type="xsd:string" /> + <xsd:attribute name="type" type="xsd:string" /> + <xsd:attribute name="mimetype" type="xsd:string" /> + <xsd:attribute ref="xml:space" /> + </xsd:complexType> + </xsd:element> + <xsd:element name="assembly"> + <xsd:complexType> + <xsd:attribute name="alias" type="xsd:string" /> + <xsd:attribute name="name" type="xsd:string" /> + </xsd:complexType> + </xsd:element> + <xsd:element name="data"> + <xsd:complexType> + <xsd:sequence> + <xsd:element name="value" type="xsd:string" minOccurs="0" msdata:Ordinal="1" /> + <xsd:element name="comment" type="xsd:string" minOccurs="0" msdata:Ordinal="2" /> + </xsd:sequence> + <xsd:attribute name="name" type="xsd:string" use="required" msdata:Ordinal="1" /> + <xsd:attribute name="type" type="xsd:string" msdata:Ordinal="3" /> + <xsd:attribute name="mimetype" type="xsd:string" msdata:Ordinal="4" /> + <xsd:attribute ref="xml:space" /> + </xsd:complexType> + </xsd:element> + <xsd:element name="resheader"> + <xsd:complexType> + <xsd:sequence> + <xsd:element name="value" type="xsd:string" minOccurs="0" msdata:Ordinal="1" /> + </xsd:sequence> + <xsd:attribute name="name" type="xsd:string" use="required" /> + </xsd:complexType> + </xsd:element> + </xsd:choice> + </xsd:complexType> + </xsd:element> + </xsd:schema> + <resheader name="resmimetype"> + <value>text/microsoft-resx</value> + </resheader> + <resheader name="version"> + <value>2.0</value> + </resheader> + <resheader name="reader"> + <value>System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value> + </resheader> + <resheader name="writer"> + <value>System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value> + </resheader> + <data name="Cancel" xml:space="preserve"> + <value>Annulla</value> + <comment>ContextActionCell Cancel button</comment> + </data> + <data name="More" xml:space="preserve"> + <value>Altro</value> + <comment>ContextActionCell More button</comment> + </data> +</root>
\ No newline at end of file diff --git a/Xamarin.Forms.Platform.iOS/Resources/StringResources.ja.resx b/Xamarin.Forms.Platform.iOS/Resources/StringResources.ja.resx new file mode 100644 index 00000000..aa6ed1a4 --- /dev/null +++ b/Xamarin.Forms.Platform.iOS/Resources/StringResources.ja.resx @@ -0,0 +1,128 @@ +<?xml version="1.0" encoding="utf-8"?> +<root> + <!-- + Microsoft ResX Schema + + Version 2.0 + + The primary goals of this format is to allow a simple XML format + that is mostly human readable. The generation and parsing of the + various data types are done through the TypeConverter classes + associated with the data types. + + Example: + + ... ado.net/XML headers & schema ... + <resheader name="resmimetype">text/microsoft-resx</resheader> + <resheader name="version">2.0</resheader> + <resheader name="reader">System.Resources.ResXResourceReader, System.Windows.Forms, ...</resheader> + <resheader name="writer">System.Resources.ResXResourceWriter, System.Windows.Forms, ...</resheader> + <data name="Name1"><value>this is my long string</value><comment>this is a comment</comment></data> + <data name="Color1" type="System.Drawing.Color, System.Drawing">Blue</data> + <data name="Bitmap1" mimetype="application/x-microsoft.net.object.binary.base64"> + <value>[base64 mime encoded serialized .NET Framework object]</value> + </data> + <data name="Icon1" type="System.Drawing.Icon, System.Drawing" mimetype="application/x-microsoft.net.object.bytearray.base64"> + <value>[base64 mime encoded string representing a byte array form of the .NET Framework object]</value> + <comment>This is a comment</comment> + </data> + + There are any number of "resheader" rows that contain simple + name/value pairs. + + Each data row contains a name, and value. The row also contains a + type or mimetype. Type corresponds to a .NET class that support + text/value conversion through the TypeConverter architecture. + Classes that don't support this are serialized and stored with the + mimetype set. + + The mimetype is used for serialized objects, and tells the + ResXResourceReader how to depersist the object. This is currently not + extensible. For a given mimetype the value must be set accordingly: + + Note - application/x-microsoft.net.object.binary.base64 is the format + that the ResXResourceWriter will generate, however the reader can + read any of the formats listed below. + + mimetype: application/x-microsoft.net.object.binary.base64 + value : The object must be serialized with + : System.Runtime.Serialization.Formatters.Binary.BinaryFormatter + : and then encoded with base64 encoding. + + mimetype: application/x-microsoft.net.object.soap.base64 + value : The object must be serialized with + : System.Runtime.Serialization.Formatters.Soap.SoapFormatter + : and then encoded with base64 encoding. + + mimetype: application/x-microsoft.net.object.bytearray.base64 + value : The object must be serialized into a byte array + : using a System.ComponentModel.TypeConverter + : and then encoded with base64 encoding. + --> + <xsd:schema id="root" xmlns="" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:msdata="urn:schemas-microsoft-com:xml-msdata"> + <xsd:import namespace="http://www.w3.org/XML/1998/namespace" /> + <xsd:element name="root" msdata:IsDataSet="true"> + <xsd:complexType> + <xsd:choice maxOccurs="unbounded"> + <xsd:element name="metadata"> + <xsd:complexType> + <xsd:sequence> + <xsd:element name="value" type="xsd:string" minOccurs="0" /> + </xsd:sequence> + <xsd:attribute name="name" use="required" type="xsd:string" /> + <xsd:attribute name="type" type="xsd:string" /> + <xsd:attribute name="mimetype" type="xsd:string" /> + <xsd:attribute ref="xml:space" /> + </xsd:complexType> + </xsd:element> + <xsd:element name="assembly"> + <xsd:complexType> + <xsd:attribute name="alias" type="xsd:string" /> + <xsd:attribute name="name" type="xsd:string" /> + </xsd:complexType> + </xsd:element> + <xsd:element name="data"> + <xsd:complexType> + <xsd:sequence> + <xsd:element name="value" type="xsd:string" minOccurs="0" msdata:Ordinal="1" /> + <xsd:element name="comment" type="xsd:string" minOccurs="0" msdata:Ordinal="2" /> + </xsd:sequence> + <xsd:attribute name="name" type="xsd:string" use="required" msdata:Ordinal="1" /> + <xsd:attribute name="type" type="xsd:string" msdata:Ordinal="3" /> + <xsd:attribute name="mimetype" type="xsd:string" msdata:Ordinal="4" /> + <xsd:attribute ref="xml:space" /> + </xsd:complexType> + </xsd:element> + <xsd:element name="resheader"> + <xsd:complexType> + <xsd:sequence> + <xsd:element name="value" type="xsd:string" minOccurs="0" msdata:Ordinal="1" /> + </xsd:sequence> + <xsd:attribute name="name" type="xsd:string" use="required" /> + </xsd:complexType> + </xsd:element> + </xsd:choice> + </xsd:complexType> + </xsd:element> + </xsd:schema> + <resheader name="resmimetype"> + <value>text/microsoft-resx</value> + </resheader> + <resheader name="version"> + <value>2.0</value> + </resheader> + <resheader name="reader"> + <value>System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value> + </resheader> + <resheader name="writer"> + <value>System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value> + </resheader> + <data name="Cancel" xml:space="preserve"> + <value>キャンセル</value> + <comment>ContextActionCell Cancel button</comment> + </data> + <data name="More" xml:space="preserve"> + <value>その他</value> + <comment>ContextActionCell More button</comment> + </data> +</root>
\ No newline at end of file diff --git a/Xamarin.Forms.Platform.iOS/Resources/StringResources.ko.resx b/Xamarin.Forms.Platform.iOS/Resources/StringResources.ko.resx new file mode 100644 index 00000000..2a2de740 --- /dev/null +++ b/Xamarin.Forms.Platform.iOS/Resources/StringResources.ko.resx @@ -0,0 +1,128 @@ +<?xml version="1.0" encoding="utf-8"?> +<root> + <!-- + Microsoft ResX Schema + + Version 2.0 + + The primary goals of this format is to allow a simple XML format + that is mostly human readable. The generation and parsing of the + various data types are done through the TypeConverter classes + associated with the data types. + + Example: + + ... ado.net/XML headers & schema ... + <resheader name="resmimetype">text/microsoft-resx</resheader> + <resheader name="version">2.0</resheader> + <resheader name="reader">System.Resources.ResXResourceReader, System.Windows.Forms, ...</resheader> + <resheader name="writer">System.Resources.ResXResourceWriter, System.Windows.Forms, ...</resheader> + <data name="Name1"><value>this is my long string</value><comment>this is a comment</comment></data> + <data name="Color1" type="System.Drawing.Color, System.Drawing">Blue</data> + <data name="Bitmap1" mimetype="application/x-microsoft.net.object.binary.base64"> + <value>[base64 mime encoded serialized .NET Framework object]</value> + </data> + <data name="Icon1" type="System.Drawing.Icon, System.Drawing" mimetype="application/x-microsoft.net.object.bytearray.base64"> + <value>[base64 mime encoded string representing a byte array form of the .NET Framework object]</value> + <comment>This is a comment</comment> + </data> + + There are any number of "resheader" rows that contain simple + name/value pairs. + + Each data row contains a name, and value. The row also contains a + type or mimetype. Type corresponds to a .NET class that support + text/value conversion through the TypeConverter architecture. + Classes that don't support this are serialized and stored with the + mimetype set. + + The mimetype is used for serialized objects, and tells the + ResXResourceReader how to depersist the object. This is currently not + extensible. For a given mimetype the value must be set accordingly: + + Note - application/x-microsoft.net.object.binary.base64 is the format + that the ResXResourceWriter will generate, however the reader can + read any of the formats listed below. + + mimetype: application/x-microsoft.net.object.binary.base64 + value : The object must be serialized with + : System.Runtime.Serialization.Formatters.Binary.BinaryFormatter + : and then encoded with base64 encoding. + + mimetype: application/x-microsoft.net.object.soap.base64 + value : The object must be serialized with + : System.Runtime.Serialization.Formatters.Soap.SoapFormatter + : and then encoded with base64 encoding. + + mimetype: application/x-microsoft.net.object.bytearray.base64 + value : The object must be serialized into a byte array + : using a System.ComponentModel.TypeConverter + : and then encoded with base64 encoding. + --> + <xsd:schema id="root" xmlns="" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:msdata="urn:schemas-microsoft-com:xml-msdata"> + <xsd:import namespace="http://www.w3.org/XML/1998/namespace" /> + <xsd:element name="root" msdata:IsDataSet="true"> + <xsd:complexType> + <xsd:choice maxOccurs="unbounded"> + <xsd:element name="metadata"> + <xsd:complexType> + <xsd:sequence> + <xsd:element name="value" type="xsd:string" minOccurs="0" /> + </xsd:sequence> + <xsd:attribute name="name" use="required" type="xsd:string" /> + <xsd:attribute name="type" type="xsd:string" /> + <xsd:attribute name="mimetype" type="xsd:string" /> + <xsd:attribute ref="xml:space" /> + </xsd:complexType> + </xsd:element> + <xsd:element name="assembly"> + <xsd:complexType> + <xsd:attribute name="alias" type="xsd:string" /> + <xsd:attribute name="name" type="xsd:string" /> + </xsd:complexType> + </xsd:element> + <xsd:element name="data"> + <xsd:complexType> + <xsd:sequence> + <xsd:element name="value" type="xsd:string" minOccurs="0" msdata:Ordinal="1" /> + <xsd:element name="comment" type="xsd:string" minOccurs="0" msdata:Ordinal="2" /> + </xsd:sequence> + <xsd:attribute name="name" type="xsd:string" use="required" msdata:Ordinal="1" /> + <xsd:attribute name="type" type="xsd:string" msdata:Ordinal="3" /> + <xsd:attribute name="mimetype" type="xsd:string" msdata:Ordinal="4" /> + <xsd:attribute ref="xml:space" /> + </xsd:complexType> + </xsd:element> + <xsd:element name="resheader"> + <xsd:complexType> + <xsd:sequence> + <xsd:element name="value" type="xsd:string" minOccurs="0" msdata:Ordinal="1" /> + </xsd:sequence> + <xsd:attribute name="name" type="xsd:string" use="required" /> + </xsd:complexType> + </xsd:element> + </xsd:choice> + </xsd:complexType> + </xsd:element> + </xsd:schema> + <resheader name="resmimetype"> + <value>text/microsoft-resx</value> + </resheader> + <resheader name="version"> + <value>2.0</value> + </resheader> + <resheader name="reader"> + <value>System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value> + </resheader> + <resheader name="writer"> + <value>System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value> + </resheader> + <data name="Cancel" xml:space="preserve"> + <value>취소</value> + <comment>ContextActionCell Cancel button</comment> + </data> + <data name="More" xml:space="preserve"> + <value>기타</value> + <comment>ContextActionCell More button</comment> + </data> +</root>
\ No newline at end of file diff --git a/Xamarin.Forms.Platform.iOS/Resources/StringResources.ms.resx b/Xamarin.Forms.Platform.iOS/Resources/StringResources.ms.resx new file mode 100644 index 00000000..fd679653 --- /dev/null +++ b/Xamarin.Forms.Platform.iOS/Resources/StringResources.ms.resx @@ -0,0 +1,128 @@ +<?xml version="1.0" encoding="utf-8"?> +<root> + <!-- + Microsoft ResX Schema + + Version 2.0 + + The primary goals of this format is to allow a simple XML format + that is mostly human readable. The generation and parsing of the + various data types are done through the TypeConverter classes + associated with the data types. + + Example: + + ... ado.net/XML headers & schema ... + <resheader name="resmimetype">text/microsoft-resx</resheader> + <resheader name="version">2.0</resheader> + <resheader name="reader">System.Resources.ResXResourceReader, System.Windows.Forms, ...</resheader> + <resheader name="writer">System.Resources.ResXResourceWriter, System.Windows.Forms, ...</resheader> + <data name="Name1"><value>this is my long string</value><comment>this is a comment</comment></data> + <data name="Color1" type="System.Drawing.Color, System.Drawing">Blue</data> + <data name="Bitmap1" mimetype="application/x-microsoft.net.object.binary.base64"> + <value>[base64 mime encoded serialized .NET Framework object]</value> + </data> + <data name="Icon1" type="System.Drawing.Icon, System.Drawing" mimetype="application/x-microsoft.net.object.bytearray.base64"> + <value>[base64 mime encoded string representing a byte array form of the .NET Framework object]</value> + <comment>This is a comment</comment> + </data> + + There are any number of "resheader" rows that contain simple + name/value pairs. + + Each data row contains a name, and value. The row also contains a + type or mimetype. Type corresponds to a .NET class that support + text/value conversion through the TypeConverter architecture. + Classes that don't support this are serialized and stored with the + mimetype set. + + The mimetype is used for serialized objects, and tells the + ResXResourceReader how to depersist the object. This is currently not + extensible. For a given mimetype the value must be set accordingly: + + Note - application/x-microsoft.net.object.binary.base64 is the format + that the ResXResourceWriter will generate, however the reader can + read any of the formats listed below. + + mimetype: application/x-microsoft.net.object.binary.base64 + value : The object must be serialized with + : System.Runtime.Serialization.Formatters.Binary.BinaryFormatter + : and then encoded with base64 encoding. + + mimetype: application/x-microsoft.net.object.soap.base64 + value : The object must be serialized with + : System.Runtime.Serialization.Formatters.Soap.SoapFormatter + : and then encoded with base64 encoding. + + mimetype: application/x-microsoft.net.object.bytearray.base64 + value : The object must be serialized into a byte array + : using a System.ComponentModel.TypeConverter + : and then encoded with base64 encoding. + --> + <xsd:schema id="root" xmlns="" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:msdata="urn:schemas-microsoft-com:xml-msdata"> + <xsd:import namespace="http://www.w3.org/XML/1998/namespace" /> + <xsd:element name="root" msdata:IsDataSet="true"> + <xsd:complexType> + <xsd:choice maxOccurs="unbounded"> + <xsd:element name="metadata"> + <xsd:complexType> + <xsd:sequence> + <xsd:element name="value" type="xsd:string" minOccurs="0" /> + </xsd:sequence> + <xsd:attribute name="name" use="required" type="xsd:string" /> + <xsd:attribute name="type" type="xsd:string" /> + <xsd:attribute name="mimetype" type="xsd:string" /> + <xsd:attribute ref="xml:space" /> + </xsd:complexType> + </xsd:element> + <xsd:element name="assembly"> + <xsd:complexType> + <xsd:attribute name="alias" type="xsd:string" /> + <xsd:attribute name="name" type="xsd:string" /> + </xsd:complexType> + </xsd:element> + <xsd:element name="data"> + <xsd:complexType> + <xsd:sequence> + <xsd:element name="value" type="xsd:string" minOccurs="0" msdata:Ordinal="1" /> + <xsd:element name="comment" type="xsd:string" minOccurs="0" msdata:Ordinal="2" /> + </xsd:sequence> + <xsd:attribute name="name" type="xsd:string" use="required" msdata:Ordinal="1" /> + <xsd:attribute name="type" type="xsd:string" msdata:Ordinal="3" /> + <xsd:attribute name="mimetype" type="xsd:string" msdata:Ordinal="4" /> + <xsd:attribute ref="xml:space" /> + </xsd:complexType> + </xsd:element> + <xsd:element name="resheader"> + <xsd:complexType> + <xsd:sequence> + <xsd:element name="value" type="xsd:string" minOccurs="0" msdata:Ordinal="1" /> + </xsd:sequence> + <xsd:attribute name="name" type="xsd:string" use="required" /> + </xsd:complexType> + </xsd:element> + </xsd:choice> + </xsd:complexType> + </xsd:element> + </xsd:schema> + <resheader name="resmimetype"> + <value>text/microsoft-resx</value> + </resheader> + <resheader name="version"> + <value>2.0</value> + </resheader> + <resheader name="reader"> + <value>System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value> + </resheader> + <resheader name="writer"> + <value>System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value> + </resheader> + <data name="Cancel" xml:space="preserve"> + <value>Batal</value> + <comment>ContextActionCell Cancel button</comment> + </data> + <data name="More" xml:space="preserve"> + <value>Lagi</value> + <comment>ContextActionCell More button</comment> + </data> +</root>
\ No newline at end of file diff --git a/Xamarin.Forms.Platform.iOS/Resources/StringResources.nb.resx b/Xamarin.Forms.Platform.iOS/Resources/StringResources.nb.resx new file mode 100644 index 00000000..671f06f6 --- /dev/null +++ b/Xamarin.Forms.Platform.iOS/Resources/StringResources.nb.resx @@ -0,0 +1,128 @@ +<?xml version="1.0" encoding="utf-8"?> +<root> + <!-- + Microsoft ResX Schema + + Version 2.0 + + The primary goals of this format is to allow a simple XML format + that is mostly human readable. The generation and parsing of the + various data types are done through the TypeConverter classes + associated with the data types. + + Example: + + ... ado.net/XML headers & schema ... + <resheader name="resmimetype">text/microsoft-resx</resheader> + <resheader name="version">2.0</resheader> + <resheader name="reader">System.Resources.ResXResourceReader, System.Windows.Forms, ...</resheader> + <resheader name="writer">System.Resources.ResXResourceWriter, System.Windows.Forms, ...</resheader> + <data name="Name1"><value>this is my long string</value><comment>this is a comment</comment></data> + <data name="Color1" type="System.Drawing.Color, System.Drawing">Blue</data> + <data name="Bitmap1" mimetype="application/x-microsoft.net.object.binary.base64"> + <value>[base64 mime encoded serialized .NET Framework object]</value> + </data> + <data name="Icon1" type="System.Drawing.Icon, System.Drawing" mimetype="application/x-microsoft.net.object.bytearray.base64"> + <value>[base64 mime encoded string representing a byte array form of the .NET Framework object]</value> + <comment>This is a comment</comment> + </data> + + There are any number of "resheader" rows that contain simple + name/value pairs. + + Each data row contains a name, and value. The row also contains a + type or mimetype. Type corresponds to a .NET class that support + text/value conversion through the TypeConverter architecture. + Classes that don't support this are serialized and stored with the + mimetype set. + + The mimetype is used for serialized objects, and tells the + ResXResourceReader how to depersist the object. This is currently not + extensible. For a given mimetype the value must be set accordingly: + + Note - application/x-microsoft.net.object.binary.base64 is the format + that the ResXResourceWriter will generate, however the reader can + read any of the formats listed below. + + mimetype: application/x-microsoft.net.object.binary.base64 + value : The object must be serialized with + : System.Runtime.Serialization.Formatters.Binary.BinaryFormatter + : and then encoded with base64 encoding. + + mimetype: application/x-microsoft.net.object.soap.base64 + value : The object must be serialized with + : System.Runtime.Serialization.Formatters.Soap.SoapFormatter + : and then encoded with base64 encoding. + + mimetype: application/x-microsoft.net.object.bytearray.base64 + value : The object must be serialized into a byte array + : using a System.ComponentModel.TypeConverter + : and then encoded with base64 encoding. + --> + <xsd:schema id="root" xmlns="" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:msdata="urn:schemas-microsoft-com:xml-msdata"> + <xsd:import namespace="http://www.w3.org/XML/1998/namespace" /> + <xsd:element name="root" msdata:IsDataSet="true"> + <xsd:complexType> + <xsd:choice maxOccurs="unbounded"> + <xsd:element name="metadata"> + <xsd:complexType> + <xsd:sequence> + <xsd:element name="value" type="xsd:string" minOccurs="0" /> + </xsd:sequence> + <xsd:attribute name="name" use="required" type="xsd:string" /> + <xsd:attribute name="type" type="xsd:string" /> + <xsd:attribute name="mimetype" type="xsd:string" /> + <xsd:attribute ref="xml:space" /> + </xsd:complexType> + </xsd:element> + <xsd:element name="assembly"> + <xsd:complexType> + <xsd:attribute name="alias" type="xsd:string" /> + <xsd:attribute name="name" type="xsd:string" /> + </xsd:complexType> + </xsd:element> + <xsd:element name="data"> + <xsd:complexType> + <xsd:sequence> + <xsd:element name="value" type="xsd:string" minOccurs="0" msdata:Ordinal="1" /> + <xsd:element name="comment" type="xsd:string" minOccurs="0" msdata:Ordinal="2" /> + </xsd:sequence> + <xsd:attribute name="name" type="xsd:string" use="required" msdata:Ordinal="1" /> + <xsd:attribute name="type" type="xsd:string" msdata:Ordinal="3" /> + <xsd:attribute name="mimetype" type="xsd:string" msdata:Ordinal="4" /> + <xsd:attribute ref="xml:space" /> + </xsd:complexType> + </xsd:element> + <xsd:element name="resheader"> + <xsd:complexType> + <xsd:sequence> + <xsd:element name="value" type="xsd:string" minOccurs="0" msdata:Ordinal="1" /> + </xsd:sequence> + <xsd:attribute name="name" type="xsd:string" use="required" /> + </xsd:complexType> + </xsd:element> + </xsd:choice> + </xsd:complexType> + </xsd:element> + </xsd:schema> + <resheader name="resmimetype"> + <value>text/microsoft-resx</value> + </resheader> + <resheader name="version"> + <value>2.0</value> + </resheader> + <resheader name="reader"> + <value>System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value> + </resheader> + <resheader name="writer"> + <value>System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value> + </resheader> + <data name="Cancel" xml:space="preserve"> + <value>Avbryt</value> + <comment>ContextActionCell Cancel button</comment> + </data> + <data name="More" xml:space="preserve"> + <value>Mer</value> + <comment>ContextActionCell More button</comment> + </data> +</root>
\ No newline at end of file diff --git a/Xamarin.Forms.Platform.iOS/Resources/StringResources.nl.resx b/Xamarin.Forms.Platform.iOS/Resources/StringResources.nl.resx new file mode 100644 index 00000000..dd592bac --- /dev/null +++ b/Xamarin.Forms.Platform.iOS/Resources/StringResources.nl.resx @@ -0,0 +1,128 @@ +<?xml version="1.0" encoding="utf-8"?> +<root> + <!-- + Microsoft ResX Schema + + Version 2.0 + + The primary goals of this format is to allow a simple XML format + that is mostly human readable. The generation and parsing of the + various data types are done through the TypeConverter classes + associated with the data types. + + Example: + + ... ado.net/XML headers & schema ... + <resheader name="resmimetype">text/microsoft-resx</resheader> + <resheader name="version">2.0</resheader> + <resheader name="reader">System.Resources.ResXResourceReader, System.Windows.Forms, ...</resheader> + <resheader name="writer">System.Resources.ResXResourceWriter, System.Windows.Forms, ...</resheader> + <data name="Name1"><value>this is my long string</value><comment>this is a comment</comment></data> + <data name="Color1" type="System.Drawing.Color, System.Drawing">Blue</data> + <data name="Bitmap1" mimetype="application/x-microsoft.net.object.binary.base64"> + <value>[base64 mime encoded serialized .NET Framework object]</value> + </data> + <data name="Icon1" type="System.Drawing.Icon, System.Drawing" mimetype="application/x-microsoft.net.object.bytearray.base64"> + <value>[base64 mime encoded string representing a byte array form of the .NET Framework object]</value> + <comment>This is a comment</comment> + </data> + + There are any number of "resheader" rows that contain simple + name/value pairs. + + Each data row contains a name, and value. The row also contains a + type or mimetype. Type corresponds to a .NET class that support + text/value conversion through the TypeConverter architecture. + Classes that don't support this are serialized and stored with the + mimetype set. + + The mimetype is used for serialized objects, and tells the + ResXResourceReader how to depersist the object. This is currently not + extensible. For a given mimetype the value must be set accordingly: + + Note - application/x-microsoft.net.object.binary.base64 is the format + that the ResXResourceWriter will generate, however the reader can + read any of the formats listed below. + + mimetype: application/x-microsoft.net.object.binary.base64 + value : The object must be serialized with + : System.Runtime.Serialization.Formatters.Binary.BinaryFormatter + : and then encoded with base64 encoding. + + mimetype: application/x-microsoft.net.object.soap.base64 + value : The object must be serialized with + : System.Runtime.Serialization.Formatters.Soap.SoapFormatter + : and then encoded with base64 encoding. + + mimetype: application/x-microsoft.net.object.bytearray.base64 + value : The object must be serialized into a byte array + : using a System.ComponentModel.TypeConverter + : and then encoded with base64 encoding. + --> + <xsd:schema id="root" xmlns="" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:msdata="urn:schemas-microsoft-com:xml-msdata"> + <xsd:import namespace="http://www.w3.org/XML/1998/namespace" /> + <xsd:element name="root" msdata:IsDataSet="true"> + <xsd:complexType> + <xsd:choice maxOccurs="unbounded"> + <xsd:element name="metadata"> + <xsd:complexType> + <xsd:sequence> + <xsd:element name="value" type="xsd:string" minOccurs="0" /> + </xsd:sequence> + <xsd:attribute name="name" use="required" type="xsd:string" /> + <xsd:attribute name="type" type="xsd:string" /> + <xsd:attribute name="mimetype" type="xsd:string" /> + <xsd:attribute ref="xml:space" /> + </xsd:complexType> + </xsd:element> + <xsd:element name="assembly"> + <xsd:complexType> + <xsd:attribute name="alias" type="xsd:string" /> + <xsd:attribute name="name" type="xsd:string" /> + </xsd:complexType> + </xsd:element> + <xsd:element name="data"> + <xsd:complexType> + <xsd:sequence> + <xsd:element name="value" type="xsd:string" minOccurs="0" msdata:Ordinal="1" /> + <xsd:element name="comment" type="xsd:string" minOccurs="0" msdata:Ordinal="2" /> + </xsd:sequence> + <xsd:attribute name="name" type="xsd:string" use="required" msdata:Ordinal="1" /> + <xsd:attribute name="type" type="xsd:string" msdata:Ordinal="3" /> + <xsd:attribute name="mimetype" type="xsd:string" msdata:Ordinal="4" /> + <xsd:attribute ref="xml:space" /> + </xsd:complexType> + </xsd:element> + <xsd:element name="resheader"> + <xsd:complexType> + <xsd:sequence> + <xsd:element name="value" type="xsd:string" minOccurs="0" msdata:Ordinal="1" /> + </xsd:sequence> + <xsd:attribute name="name" type="xsd:string" use="required" /> + </xsd:complexType> + </xsd:element> + </xsd:choice> + </xsd:complexType> + </xsd:element> + </xsd:schema> + <resheader name="resmimetype"> + <value>text/microsoft-resx</value> + </resheader> + <resheader name="version"> + <value>2.0</value> + </resheader> + <resheader name="reader"> + <value>System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value> + </resheader> + <resheader name="writer"> + <value>System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value> + </resheader> + <data name="Cancel" xml:space="preserve"> + <value>Annuleer</value> + <comment>ContextActionCell Cancel button</comment> + </data> + <data name="More" xml:space="preserve"> + <value>Meer</value> + <comment>ContextActionCell More button</comment> + </data> +</root>
\ No newline at end of file diff --git a/Xamarin.Forms.Platform.iOS/Resources/StringResources.pl.resx b/Xamarin.Forms.Platform.iOS/Resources/StringResources.pl.resx new file mode 100644 index 00000000..5a99c8e1 --- /dev/null +++ b/Xamarin.Forms.Platform.iOS/Resources/StringResources.pl.resx @@ -0,0 +1,128 @@ +<?xml version="1.0" encoding="utf-8"?> +<root> + <!-- + Microsoft ResX Schema + + Version 2.0 + + The primary goals of this format is to allow a simple XML format + that is mostly human readable. The generation and parsing of the + various data types are done through the TypeConverter classes + associated with the data types. + + Example: + + ... ado.net/XML headers & schema ... + <resheader name="resmimetype">text/microsoft-resx</resheader> + <resheader name="version">2.0</resheader> + <resheader name="reader">System.Resources.ResXResourceReader, System.Windows.Forms, ...</resheader> + <resheader name="writer">System.Resources.ResXResourceWriter, System.Windows.Forms, ...</resheader> + <data name="Name1"><value>this is my long string</value><comment>this is a comment</comment></data> + <data name="Color1" type="System.Drawing.Color, System.Drawing">Blue</data> + <data name="Bitmap1" mimetype="application/x-microsoft.net.object.binary.base64"> + <value>[base64 mime encoded serialized .NET Framework object]</value> + </data> + <data name="Icon1" type="System.Drawing.Icon, System.Drawing" mimetype="application/x-microsoft.net.object.bytearray.base64"> + <value>[base64 mime encoded string representing a byte array form of the .NET Framework object]</value> + <comment>This is a comment</comment> + </data> + + There are any number of "resheader" rows that contain simple + name/value pairs. + + Each data row contains a name, and value. The row also contains a + type or mimetype. Type corresponds to a .NET class that support + text/value conversion through the TypeConverter architecture. + Classes that don't support this are serialized and stored with the + mimetype set. + + The mimetype is used for serialized objects, and tells the + ResXResourceReader how to depersist the object. This is currently not + extensible. For a given mimetype the value must be set accordingly: + + Note - application/x-microsoft.net.object.binary.base64 is the format + that the ResXResourceWriter will generate, however the reader can + read any of the formats listed below. + + mimetype: application/x-microsoft.net.object.binary.base64 + value : The object must be serialized with + : System.Runtime.Serialization.Formatters.Binary.BinaryFormatter + : and then encoded with base64 encoding. + + mimetype: application/x-microsoft.net.object.soap.base64 + value : The object must be serialized with + : System.Runtime.Serialization.Formatters.Soap.SoapFormatter + : and then encoded with base64 encoding. + + mimetype: application/x-microsoft.net.object.bytearray.base64 + value : The object must be serialized into a byte array + : using a System.ComponentModel.TypeConverter + : and then encoded with base64 encoding. + --> + <xsd:schema id="root" xmlns="" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:msdata="urn:schemas-microsoft-com:xml-msdata"> + <xsd:import namespace="http://www.w3.org/XML/1998/namespace" /> + <xsd:element name="root" msdata:IsDataSet="true"> + <xsd:complexType> + <xsd:choice maxOccurs="unbounded"> + <xsd:element name="metadata"> + <xsd:complexType> + <xsd:sequence> + <xsd:element name="value" type="xsd:string" minOccurs="0" /> + </xsd:sequence> + <xsd:attribute name="name" use="required" type="xsd:string" /> + <xsd:attribute name="type" type="xsd:string" /> + <xsd:attribute name="mimetype" type="xsd:string" /> + <xsd:attribute ref="xml:space" /> + </xsd:complexType> + </xsd:element> + <xsd:element name="assembly"> + <xsd:complexType> + <xsd:attribute name="alias" type="xsd:string" /> + <xsd:attribute name="name" type="xsd:string" /> + </xsd:complexType> + </xsd:element> + <xsd:element name="data"> + <xsd:complexType> + <xsd:sequence> + <xsd:element name="value" type="xsd:string" minOccurs="0" msdata:Ordinal="1" /> + <xsd:element name="comment" type="xsd:string" minOccurs="0" msdata:Ordinal="2" /> + </xsd:sequence> + <xsd:attribute name="name" type="xsd:string" use="required" msdata:Ordinal="1" /> + <xsd:attribute name="type" type="xsd:string" msdata:Ordinal="3" /> + <xsd:attribute name="mimetype" type="xsd:string" msdata:Ordinal="4" /> + <xsd:attribute ref="xml:space" /> + </xsd:complexType> + </xsd:element> + <xsd:element name="resheader"> + <xsd:complexType> + <xsd:sequence> + <xsd:element name="value" type="xsd:string" minOccurs="0" msdata:Ordinal="1" /> + </xsd:sequence> + <xsd:attribute name="name" type="xsd:string" use="required" /> + </xsd:complexType> + </xsd:element> + </xsd:choice> + </xsd:complexType> + </xsd:element> + </xsd:schema> + <resheader name="resmimetype"> + <value>text/microsoft-resx</value> + </resheader> + <resheader name="version"> + <value>2.0</value> + </resheader> + <resheader name="reader"> + <value>System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value> + </resheader> + <resheader name="writer"> + <value>System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value> + </resheader> + <data name="Cancel" xml:space="preserve"> + <value>Anuluj</value> + <comment>ContextActionCell Cancel button</comment> + </data> + <data name="More" xml:space="preserve"> + <value>Więcej</value> + <comment>ContextActionCell More button</comment> + </data> +</root>
\ No newline at end of file diff --git a/Xamarin.Forms.Platform.iOS/Resources/StringResources.pt-BR.resx b/Xamarin.Forms.Platform.iOS/Resources/StringResources.pt-BR.resx new file mode 100644 index 00000000..38182acf --- /dev/null +++ b/Xamarin.Forms.Platform.iOS/Resources/StringResources.pt-BR.resx @@ -0,0 +1,128 @@ +<?xml version="1.0" encoding="utf-8"?> +<root> + <!-- + Microsoft ResX Schema + + Version 2.0 + + The primary goals of this format is to allow a simple XML format + that is mostly human readable. The generation and parsing of the + various data types are done through the TypeConverter classes + associated with the data types. + + Example: + + ... ado.net/XML headers & schema ... + <resheader name="resmimetype">text/microsoft-resx</resheader> + <resheader name="version">2.0</resheader> + <resheader name="reader">System.Resources.ResXResourceReader, System.Windows.Forms, ...</resheader> + <resheader name="writer">System.Resources.ResXResourceWriter, System.Windows.Forms, ...</resheader> + <data name="Name1"><value>this is my long string</value><comment>this is a comment</comment></data> + <data name="Color1" type="System.Drawing.Color, System.Drawing">Blue</data> + <data name="Bitmap1" mimetype="application/x-microsoft.net.object.binary.base64"> + <value>[base64 mime encoded serialized .NET Framework object]</value> + </data> + <data name="Icon1" type="System.Drawing.Icon, System.Drawing" mimetype="application/x-microsoft.net.object.bytearray.base64"> + <value>[base64 mime encoded string representing a byte array form of the .NET Framework object]</value> + <comment>This is a comment</comment> + </data> + + There are any number of "resheader" rows that contain simple + name/value pairs. + + Each data row contains a name, and value. The row also contains a + type or mimetype. Type corresponds to a .NET class that support + text/value conversion through the TypeConverter architecture. + Classes that don't support this are serialized and stored with the + mimetype set. + + The mimetype is used for serialized objects, and tells the + ResXResourceReader how to depersist the object. This is currently not + extensible. For a given mimetype the value must be set accordingly: + + Note - application/x-microsoft.net.object.binary.base64 is the format + that the ResXResourceWriter will generate, however the reader can + read any of the formats listed below. + + mimetype: application/x-microsoft.net.object.binary.base64 + value : The object must be serialized with + : System.Runtime.Serialization.Formatters.Binary.BinaryFormatter + : and then encoded with base64 encoding. + + mimetype: application/x-microsoft.net.object.soap.base64 + value : The object must be serialized with + : System.Runtime.Serialization.Formatters.Soap.SoapFormatter + : and then encoded with base64 encoding. + + mimetype: application/x-microsoft.net.object.bytearray.base64 + value : The object must be serialized into a byte array + : using a System.ComponentModel.TypeConverter + : and then encoded with base64 encoding. + --> + <xsd:schema id="root" xmlns="" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:msdata="urn:schemas-microsoft-com:xml-msdata"> + <xsd:import namespace="http://www.w3.org/XML/1998/namespace" /> + <xsd:element name="root" msdata:IsDataSet="true"> + <xsd:complexType> + <xsd:choice maxOccurs="unbounded"> + <xsd:element name="metadata"> + <xsd:complexType> + <xsd:sequence> + <xsd:element name="value" type="xsd:string" minOccurs="0" /> + </xsd:sequence> + <xsd:attribute name="name" use="required" type="xsd:string" /> + <xsd:attribute name="type" type="xsd:string" /> + <xsd:attribute name="mimetype" type="xsd:string" /> + <xsd:attribute ref="xml:space" /> + </xsd:complexType> + </xsd:element> + <xsd:element name="assembly"> + <xsd:complexType> + <xsd:attribute name="alias" type="xsd:string" /> + <xsd:attribute name="name" type="xsd:string" /> + </xsd:complexType> + </xsd:element> + <xsd:element name="data"> + <xsd:complexType> + <xsd:sequence> + <xsd:element name="value" type="xsd:string" minOccurs="0" msdata:Ordinal="1" /> + <xsd:element name="comment" type="xsd:string" minOccurs="0" msdata:Ordinal="2" /> + </xsd:sequence> + <xsd:attribute name="name" type="xsd:string" use="required" msdata:Ordinal="1" /> + <xsd:attribute name="type" type="xsd:string" msdata:Ordinal="3" /> + <xsd:attribute name="mimetype" type="xsd:string" msdata:Ordinal="4" /> + <xsd:attribute ref="xml:space" /> + </xsd:complexType> + </xsd:element> + <xsd:element name="resheader"> + <xsd:complexType> + <xsd:sequence> + <xsd:element name="value" type="xsd:string" minOccurs="0" msdata:Ordinal="1" /> + </xsd:sequence> + <xsd:attribute name="name" type="xsd:string" use="required" /> + </xsd:complexType> + </xsd:element> + </xsd:choice> + </xsd:complexType> + </xsd:element> + </xsd:schema> + <resheader name="resmimetype"> + <value>text/microsoft-resx</value> + </resheader> + <resheader name="version"> + <value>2.0</value> + </resheader> + <resheader name="reader"> + <value>System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value> + </resheader> + <resheader name="writer"> + <value>System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value> + </resheader> + <data name="Cancel" xml:space="preserve"> + <value>Cancelar</value> + <comment>ContextActionCell Cancel button</comment> + </data> + <data name="More" xml:space="preserve"> + <value>Mais</value> + <comment>ContextActionCell More button</comment> + </data> +</root>
\ No newline at end of file diff --git a/Xamarin.Forms.Platform.iOS/Resources/StringResources.pt.resx b/Xamarin.Forms.Platform.iOS/Resources/StringResources.pt.resx new file mode 100644 index 00000000..38182acf --- /dev/null +++ b/Xamarin.Forms.Platform.iOS/Resources/StringResources.pt.resx @@ -0,0 +1,128 @@ +<?xml version="1.0" encoding="utf-8"?> +<root> + <!-- + Microsoft ResX Schema + + Version 2.0 + + The primary goals of this format is to allow a simple XML format + that is mostly human readable. The generation and parsing of the + various data types are done through the TypeConverter classes + associated with the data types. + + Example: + + ... ado.net/XML headers & schema ... + <resheader name="resmimetype">text/microsoft-resx</resheader> + <resheader name="version">2.0</resheader> + <resheader name="reader">System.Resources.ResXResourceReader, System.Windows.Forms, ...</resheader> + <resheader name="writer">System.Resources.ResXResourceWriter, System.Windows.Forms, ...</resheader> + <data name="Name1"><value>this is my long string</value><comment>this is a comment</comment></data> + <data name="Color1" type="System.Drawing.Color, System.Drawing">Blue</data> + <data name="Bitmap1" mimetype="application/x-microsoft.net.object.binary.base64"> + <value>[base64 mime encoded serialized .NET Framework object]</value> + </data> + <data name="Icon1" type="System.Drawing.Icon, System.Drawing" mimetype="application/x-microsoft.net.object.bytearray.base64"> + <value>[base64 mime encoded string representing a byte array form of the .NET Framework object]</value> + <comment>This is a comment</comment> + </data> + + There are any number of "resheader" rows that contain simple + name/value pairs. + + Each data row contains a name, and value. The row also contains a + type or mimetype. Type corresponds to a .NET class that support + text/value conversion through the TypeConverter architecture. + Classes that don't support this are serialized and stored with the + mimetype set. + + The mimetype is used for serialized objects, and tells the + ResXResourceReader how to depersist the object. This is currently not + extensible. For a given mimetype the value must be set accordingly: + + Note - application/x-microsoft.net.object.binary.base64 is the format + that the ResXResourceWriter will generate, however the reader can + read any of the formats listed below. + + mimetype: application/x-microsoft.net.object.binary.base64 + value : The object must be serialized with + : System.Runtime.Serialization.Formatters.Binary.BinaryFormatter + : and then encoded with base64 encoding. + + mimetype: application/x-microsoft.net.object.soap.base64 + value : The object must be serialized with + : System.Runtime.Serialization.Formatters.Soap.SoapFormatter + : and then encoded with base64 encoding. + + mimetype: application/x-microsoft.net.object.bytearray.base64 + value : The object must be serialized into a byte array + : using a System.ComponentModel.TypeConverter + : and then encoded with base64 encoding. + --> + <xsd:schema id="root" xmlns="" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:msdata="urn:schemas-microsoft-com:xml-msdata"> + <xsd:import namespace="http://www.w3.org/XML/1998/namespace" /> + <xsd:element name="root" msdata:IsDataSet="true"> + <xsd:complexType> + <xsd:choice maxOccurs="unbounded"> + <xsd:element name="metadata"> + <xsd:complexType> + <xsd:sequence> + <xsd:element name="value" type="xsd:string" minOccurs="0" /> + </xsd:sequence> + <xsd:attribute name="name" use="required" type="xsd:string" /> + <xsd:attribute name="type" type="xsd:string" /> + <xsd:attribute name="mimetype" type="xsd:string" /> + <xsd:attribute ref="xml:space" /> + </xsd:complexType> + </xsd:element> + <xsd:element name="assembly"> + <xsd:complexType> + <xsd:attribute name="alias" type="xsd:string" /> + <xsd:attribute name="name" type="xsd:string" /> + </xsd:complexType> + </xsd:element> + <xsd:element name="data"> + <xsd:complexType> + <xsd:sequence> + <xsd:element name="value" type="xsd:string" minOccurs="0" msdata:Ordinal="1" /> + <xsd:element name="comment" type="xsd:string" minOccurs="0" msdata:Ordinal="2" /> + </xsd:sequence> + <xsd:attribute name="name" type="xsd:string" use="required" msdata:Ordinal="1" /> + <xsd:attribute name="type" type="xsd:string" msdata:Ordinal="3" /> + <xsd:attribute name="mimetype" type="xsd:string" msdata:Ordinal="4" /> + <xsd:attribute ref="xml:space" /> + </xsd:complexType> + </xsd:element> + <xsd:element name="resheader"> + <xsd:complexType> + <xsd:sequence> + <xsd:element name="value" type="xsd:string" minOccurs="0" msdata:Ordinal="1" /> + </xsd:sequence> + <xsd:attribute name="name" type="xsd:string" use="required" /> + </xsd:complexType> + </xsd:element> + </xsd:choice> + </xsd:complexType> + </xsd:element> + </xsd:schema> + <resheader name="resmimetype"> + <value>text/microsoft-resx</value> + </resheader> + <resheader name="version"> + <value>2.0</value> + </resheader> + <resheader name="reader"> + <value>System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value> + </resheader> + <resheader name="writer"> + <value>System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value> + </resheader> + <data name="Cancel" xml:space="preserve"> + <value>Cancelar</value> + <comment>ContextActionCell Cancel button</comment> + </data> + <data name="More" xml:space="preserve"> + <value>Mais</value> + <comment>ContextActionCell More button</comment> + </data> +</root>
\ No newline at end of file diff --git a/Xamarin.Forms.Platform.iOS/Resources/StringResources.resx b/Xamarin.Forms.Platform.iOS/Resources/StringResources.resx new file mode 100644 index 00000000..e96cccd2 --- /dev/null +++ b/Xamarin.Forms.Platform.iOS/Resources/StringResources.resx @@ -0,0 +1,128 @@ +<?xml version="1.0" encoding="utf-8"?> +<root> + <!-- + Microsoft ResX Schema + + Version 2.0 + + The primary goals of this format is to allow a simple XML format + that is mostly human readable. The generation and parsing of the + various data types are done through the TypeConverter classes + associated with the data types. + + Example: + + ... ado.net/XML headers & schema ... + <resheader name="resmimetype">text/microsoft-resx</resheader> + <resheader name="version">2.0</resheader> + <resheader name="reader">System.Resources.ResXResourceReader, System.Windows.Forms, ...</resheader> + <resheader name="writer">System.Resources.ResXResourceWriter, System.Windows.Forms, ...</resheader> + <data name="Name1"><value>this is my long string</value><comment>this is a comment</comment></data> + <data name="Color1" type="System.Drawing.Color, System.Drawing">Blue</data> + <data name="Bitmap1" mimetype="application/x-microsoft.net.object.binary.base64"> + <value>[base64 mime encoded serialized .NET Framework object]</value> + </data> + <data name="Icon1" type="System.Drawing.Icon, System.Drawing" mimetype="application/x-microsoft.net.object.bytearray.base64"> + <value>[base64 mime encoded string representing a byte array form of the .NET Framework object]</value> + <comment>This is a comment</comment> + </data> + + There are any number of "resheader" rows that contain simple + name/value pairs. + + Each data row contains a name, and value. The row also contains a + type or mimetype. Type corresponds to a .NET class that support + text/value conversion through the TypeConverter architecture. + Classes that don't support this are serialized and stored with the + mimetype set. + + The mimetype is used for serialized objects, and tells the + ResXResourceReader how to depersist the object. This is currently not + extensible. For a given mimetype the value must be set accordingly: + + Note - application/x-microsoft.net.object.binary.base64 is the format + that the ResXResourceWriter will generate, however the reader can + read any of the formats listed below. + + mimetype: application/x-microsoft.net.object.binary.base64 + value : The object must be serialized with + : System.Runtime.Serialization.Formatters.Binary.BinaryFormatter + : and then encoded with base64 encoding. + + mimetype: application/x-microsoft.net.object.soap.base64 + value : The object must be serialized with + : System.Runtime.Serialization.Formatters.Soap.SoapFormatter + : and then encoded with base64 encoding. + + mimetype: application/x-microsoft.net.object.bytearray.base64 + value : The object must be serialized into a byte array + : using a System.ComponentModel.TypeConverter + : and then encoded with base64 encoding. + --> + <xsd:schema id="root" xmlns="" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:msdata="urn:schemas-microsoft-com:xml-msdata"> + <xsd:import namespace="http://www.w3.org/XML/1998/namespace" /> + <xsd:element name="root" msdata:IsDataSet="true"> + <xsd:complexType> + <xsd:choice maxOccurs="unbounded"> + <xsd:element name="metadata"> + <xsd:complexType> + <xsd:sequence> + <xsd:element name="value" type="xsd:string" minOccurs="0" /> + </xsd:sequence> + <xsd:attribute name="name" use="required" type="xsd:string" /> + <xsd:attribute name="type" type="xsd:string" /> + <xsd:attribute name="mimetype" type="xsd:string" /> + <xsd:attribute ref="xml:space" /> + </xsd:complexType> + </xsd:element> + <xsd:element name="assembly"> + <xsd:complexType> + <xsd:attribute name="alias" type="xsd:string" /> + <xsd:attribute name="name" type="xsd:string" /> + </xsd:complexType> + </xsd:element> + <xsd:element name="data"> + <xsd:complexType> + <xsd:sequence> + <xsd:element name="value" type="xsd:string" minOccurs="0" msdata:Ordinal="1" /> + <xsd:element name="comment" type="xsd:string" minOccurs="0" msdata:Ordinal="2" /> + </xsd:sequence> + <xsd:attribute name="name" type="xsd:string" use="required" msdata:Ordinal="1" /> + <xsd:attribute name="type" type="xsd:string" msdata:Ordinal="3" /> + <xsd:attribute name="mimetype" type="xsd:string" msdata:Ordinal="4" /> + <xsd:attribute ref="xml:space" /> + </xsd:complexType> + </xsd:element> + <xsd:element name="resheader"> + <xsd:complexType> + <xsd:sequence> + <xsd:element name="value" type="xsd:string" minOccurs="0" msdata:Ordinal="1" /> + </xsd:sequence> + <xsd:attribute name="name" type="xsd:string" use="required" /> + </xsd:complexType> + </xsd:element> + </xsd:choice> + </xsd:complexType> + </xsd:element> + </xsd:schema> + <resheader name="resmimetype"> + <value>text/microsoft-resx</value> + </resheader> + <resheader name="version"> + <value>2.0</value> + </resheader> + <resheader name="reader"> + <value>System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value> + </resheader> + <resheader name="writer"> + <value>System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value> + </resheader> + <data name="Cancel" xml:space="preserve"> + <value>Cancel</value> + <comment>ContextActionCell Cancel button</comment> + </data> + <data name="More" xml:space="preserve"> + <value>More</value> + <comment>ContextActionCell More button</comment> + </data> +</root>
\ No newline at end of file diff --git a/Xamarin.Forms.Platform.iOS/Resources/StringResources.ro.resx b/Xamarin.Forms.Platform.iOS/Resources/StringResources.ro.resx new file mode 100644 index 00000000..0135509d --- /dev/null +++ b/Xamarin.Forms.Platform.iOS/Resources/StringResources.ro.resx @@ -0,0 +1,128 @@ +<?xml version="1.0" encoding="utf-8"?> +<root> + <!-- + Microsoft ResX Schema + + Version 2.0 + + The primary goals of this format is to allow a simple XML format + that is mostly human readable. The generation and parsing of the + various data types are done through the TypeConverter classes + associated with the data types. + + Example: + + ... ado.net/XML headers & schema ... + <resheader name="resmimetype">text/microsoft-resx</resheader> + <resheader name="version">2.0</resheader> + <resheader name="reader">System.Resources.ResXResourceReader, System.Windows.Forms, ...</resheader> + <resheader name="writer">System.Resources.ResXResourceWriter, System.Windows.Forms, ...</resheader> + <data name="Name1"><value>this is my long string</value><comment>this is a comment</comment></data> + <data name="Color1" type="System.Drawing.Color, System.Drawing">Blue</data> + <data name="Bitmap1" mimetype="application/x-microsoft.net.object.binary.base64"> + <value>[base64 mime encoded serialized .NET Framework object]</value> + </data> + <data name="Icon1" type="System.Drawing.Icon, System.Drawing" mimetype="application/x-microsoft.net.object.bytearray.base64"> + <value>[base64 mime encoded string representing a byte array form of the .NET Framework object]</value> + <comment>This is a comment</comment> + </data> + + There are any number of "resheader" rows that contain simple + name/value pairs. + + Each data row contains a name, and value. The row also contains a + type or mimetype. Type corresponds to a .NET class that support + text/value conversion through the TypeConverter architecture. + Classes that don't support this are serialized and stored with the + mimetype set. + + The mimetype is used for serialized objects, and tells the + ResXResourceReader how to depersist the object. This is currently not + extensible. For a given mimetype the value must be set accordingly: + + Note - application/x-microsoft.net.object.binary.base64 is the format + that the ResXResourceWriter will generate, however the reader can + read any of the formats listed below. + + mimetype: application/x-microsoft.net.object.binary.base64 + value : The object must be serialized with + : System.Runtime.Serialization.Formatters.Binary.BinaryFormatter + : and then encoded with base64 encoding. + + mimetype: application/x-microsoft.net.object.soap.base64 + value : The object must be serialized with + : System.Runtime.Serialization.Formatters.Soap.SoapFormatter + : and then encoded with base64 encoding. + + mimetype: application/x-microsoft.net.object.bytearray.base64 + value : The object must be serialized into a byte array + : using a System.ComponentModel.TypeConverter + : and then encoded with base64 encoding. + --> + <xsd:schema id="root" xmlns="" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:msdata="urn:schemas-microsoft-com:xml-msdata"> + <xsd:import namespace="http://www.w3.org/XML/1998/namespace" /> + <xsd:element name="root" msdata:IsDataSet="true"> + <xsd:complexType> + <xsd:choice maxOccurs="unbounded"> + <xsd:element name="metadata"> + <xsd:complexType> + <xsd:sequence> + <xsd:element name="value" type="xsd:string" minOccurs="0" /> + </xsd:sequence> + <xsd:attribute name="name" use="required" type="xsd:string" /> + <xsd:attribute name="type" type="xsd:string" /> + <xsd:attribute name="mimetype" type="xsd:string" /> + <xsd:attribute ref="xml:space" /> + </xsd:complexType> + </xsd:element> + <xsd:element name="assembly"> + <xsd:complexType> + <xsd:attribute name="alias" type="xsd:string" /> + <xsd:attribute name="name" type="xsd:string" /> + </xsd:complexType> + </xsd:element> + <xsd:element name="data"> + <xsd:complexType> + <xsd:sequence> + <xsd:element name="value" type="xsd:string" minOccurs="0" msdata:Ordinal="1" /> + <xsd:element name="comment" type="xsd:string" minOccurs="0" msdata:Ordinal="2" /> + </xsd:sequence> + <xsd:attribute name="name" type="xsd:string" use="required" msdata:Ordinal="1" /> + <xsd:attribute name="type" type="xsd:string" msdata:Ordinal="3" /> + <xsd:attribute name="mimetype" type="xsd:string" msdata:Ordinal="4" /> + <xsd:attribute ref="xml:space" /> + </xsd:complexType> + </xsd:element> + <xsd:element name="resheader"> + <xsd:complexType> + <xsd:sequence> + <xsd:element name="value" type="xsd:string" minOccurs="0" msdata:Ordinal="1" /> + </xsd:sequence> + <xsd:attribute name="name" type="xsd:string" use="required" /> + </xsd:complexType> + </xsd:element> + </xsd:choice> + </xsd:complexType> + </xsd:element> + </xsd:schema> + <resheader name="resmimetype"> + <value>text/microsoft-resx</value> + </resheader> + <resheader name="version"> + <value>2.0</value> + </resheader> + <resheader name="reader"> + <value>System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value> + </resheader> + <resheader name="writer"> + <value>System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value> + </resheader> + <data name="Cancel" xml:space="preserve"> + <value>Anulați</value> + <comment>ContextActionCell Cancel button</comment> + </data> + <data name="More" xml:space="preserve"> + <value>Altele</value> + <comment>ContextActionCell More button</comment> + </data> +</root>
\ No newline at end of file diff --git a/Xamarin.Forms.Platform.iOS/Resources/StringResources.ru.resx b/Xamarin.Forms.Platform.iOS/Resources/StringResources.ru.resx new file mode 100644 index 00000000..6789a97f --- /dev/null +++ b/Xamarin.Forms.Platform.iOS/Resources/StringResources.ru.resx @@ -0,0 +1,128 @@ +<?xml version="1.0" encoding="utf-8"?> +<root> + <!-- + Microsoft ResX Schema + + Version 2.0 + + The primary goals of this format is to allow a simple XML format + that is mostly human readable. The generation and parsing of the + various data types are done through the TypeConverter classes + associated with the data types. + + Example: + + ... ado.net/XML headers & schema ... + <resheader name="resmimetype">text/microsoft-resx</resheader> + <resheader name="version">2.0</resheader> + <resheader name="reader">System.Resources.ResXResourceReader, System.Windows.Forms, ...</resheader> + <resheader name="writer">System.Resources.ResXResourceWriter, System.Windows.Forms, ...</resheader> + <data name="Name1"><value>this is my long string</value><comment>this is a comment</comment></data> + <data name="Color1" type="System.Drawing.Color, System.Drawing">Blue</data> + <data name="Bitmap1" mimetype="application/x-microsoft.net.object.binary.base64"> + <value>[base64 mime encoded serialized .NET Framework object]</value> + </data> + <data name="Icon1" type="System.Drawing.Icon, System.Drawing" mimetype="application/x-microsoft.net.object.bytearray.base64"> + <value>[base64 mime encoded string representing a byte array form of the .NET Framework object]</value> + <comment>This is a comment</comment> + </data> + + There are any number of "resheader" rows that contain simple + name/value pairs. + + Each data row contains a name, and value. The row also contains a + type or mimetype. Type corresponds to a .NET class that support + text/value conversion through the TypeConverter architecture. + Classes that don't support this are serialized and stored with the + mimetype set. + + The mimetype is used for serialized objects, and tells the + ResXResourceReader how to depersist the object. This is currently not + extensible. For a given mimetype the value must be set accordingly: + + Note - application/x-microsoft.net.object.binary.base64 is the format + that the ResXResourceWriter will generate, however the reader can + read any of the formats listed below. + + mimetype: application/x-microsoft.net.object.binary.base64 + value : The object must be serialized with + : System.Runtime.Serialization.Formatters.Binary.BinaryFormatter + : and then encoded with base64 encoding. + + mimetype: application/x-microsoft.net.object.soap.base64 + value : The object must be serialized with + : System.Runtime.Serialization.Formatters.Soap.SoapFormatter + : and then encoded with base64 encoding. + + mimetype: application/x-microsoft.net.object.bytearray.base64 + value : The object must be serialized into a byte array + : using a System.ComponentModel.TypeConverter + : and then encoded with base64 encoding. + --> + <xsd:schema id="root" xmlns="" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:msdata="urn:schemas-microsoft-com:xml-msdata"> + <xsd:import namespace="http://www.w3.org/XML/1998/namespace" /> + <xsd:element name="root" msdata:IsDataSet="true"> + <xsd:complexType> + <xsd:choice maxOccurs="unbounded"> + <xsd:element name="metadata"> + <xsd:complexType> + <xsd:sequence> + <xsd:element name="value" type="xsd:string" minOccurs="0" /> + </xsd:sequence> + <xsd:attribute name="name" use="required" type="xsd:string" /> + <xsd:attribute name="type" type="xsd:string" /> + <xsd:attribute name="mimetype" type="xsd:string" /> + <xsd:attribute ref="xml:space" /> + </xsd:complexType> + </xsd:element> + <xsd:element name="assembly"> + <xsd:complexType> + <xsd:attribute name="alias" type="xsd:string" /> + <xsd:attribute name="name" type="xsd:string" /> + </xsd:complexType> + </xsd:element> + <xsd:element name="data"> + <xsd:complexType> + <xsd:sequence> + <xsd:element name="value" type="xsd:string" minOccurs="0" msdata:Ordinal="1" /> + <xsd:element name="comment" type="xsd:string" minOccurs="0" msdata:Ordinal="2" /> + </xsd:sequence> + <xsd:attribute name="name" type="xsd:string" use="required" msdata:Ordinal="1" /> + <xsd:attribute name="type" type="xsd:string" msdata:Ordinal="3" /> + <xsd:attribute name="mimetype" type="xsd:string" msdata:Ordinal="4" /> + <xsd:attribute ref="xml:space" /> + </xsd:complexType> + </xsd:element> + <xsd:element name="resheader"> + <xsd:complexType> + <xsd:sequence> + <xsd:element name="value" type="xsd:string" minOccurs="0" msdata:Ordinal="1" /> + </xsd:sequence> + <xsd:attribute name="name" type="xsd:string" use="required" /> + </xsd:complexType> + </xsd:element> + </xsd:choice> + </xsd:complexType> + </xsd:element> + </xsd:schema> + <resheader name="resmimetype"> + <value>text/microsoft-resx</value> + </resheader> + <resheader name="version"> + <value>2.0</value> + </resheader> + <resheader name="reader"> + <value>System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value> + </resheader> + <resheader name="writer"> + <value>System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value> + </resheader> + <data name="Cancel" xml:space="preserve"> + <value>Oтменить</value> + <comment>ContextActionCell Cancel button</comment> + </data> + <data name="More" xml:space="preserve"> + <value>Еще</value> + <comment>ContextActionCell More button</comment> + </data> +</root>
\ No newline at end of file diff --git a/Xamarin.Forms.Platform.iOS/Resources/StringResources.sk.resx b/Xamarin.Forms.Platform.iOS/Resources/StringResources.sk.resx new file mode 100644 index 00000000..17dc74f4 --- /dev/null +++ b/Xamarin.Forms.Platform.iOS/Resources/StringResources.sk.resx @@ -0,0 +1,128 @@ +<?xml version="1.0" encoding="utf-8"?> +<root> + <!-- + Microsoft ResX Schema + + Version 2.0 + + The primary goals of this format is to allow a simple XML format + that is mostly human readable. The generation and parsing of the + various data types are done through the TypeConverter classes + associated with the data types. + + Example: + + ... ado.net/XML headers & schema ... + <resheader name="resmimetype">text/microsoft-resx</resheader> + <resheader name="version">2.0</resheader> + <resheader name="reader">System.Resources.ResXResourceReader, System.Windows.Forms, ...</resheader> + <resheader name="writer">System.Resources.ResXResourceWriter, System.Windows.Forms, ...</resheader> + <data name="Name1"><value>this is my long string</value><comment>this is a comment</comment></data> + <data name="Color1" type="System.Drawing.Color, System.Drawing">Blue</data> + <data name="Bitmap1" mimetype="application/x-microsoft.net.object.binary.base64"> + <value>[base64 mime encoded serialized .NET Framework object]</value> + </data> + <data name="Icon1" type="System.Drawing.Icon, System.Drawing" mimetype="application/x-microsoft.net.object.bytearray.base64"> + <value>[base64 mime encoded string representing a byte array form of the .NET Framework object]</value> + <comment>This is a comment</comment> + </data> + + There are any number of "resheader" rows that contain simple + name/value pairs. + + Each data row contains a name, and value. The row also contains a + type or mimetype. Type corresponds to a .NET class that support + text/value conversion through the TypeConverter architecture. + Classes that don't support this are serialized and stored with the + mimetype set. + + The mimetype is used for serialized objects, and tells the + ResXResourceReader how to depersist the object. This is currently not + extensible. For a given mimetype the value must be set accordingly: + + Note - application/x-microsoft.net.object.binary.base64 is the format + that the ResXResourceWriter will generate, however the reader can + read any of the formats listed below. + + mimetype: application/x-microsoft.net.object.binary.base64 + value : The object must be serialized with + : System.Runtime.Serialization.Formatters.Binary.BinaryFormatter + : and then encoded with base64 encoding. + + mimetype: application/x-microsoft.net.object.soap.base64 + value : The object must be serialized with + : System.Runtime.Serialization.Formatters.Soap.SoapFormatter + : and then encoded with base64 encoding. + + mimetype: application/x-microsoft.net.object.bytearray.base64 + value : The object must be serialized into a byte array + : using a System.ComponentModel.TypeConverter + : and then encoded with base64 encoding. + --> + <xsd:schema id="root" xmlns="" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:msdata="urn:schemas-microsoft-com:xml-msdata"> + <xsd:import namespace="http://www.w3.org/XML/1998/namespace" /> + <xsd:element name="root" msdata:IsDataSet="true"> + <xsd:complexType> + <xsd:choice maxOccurs="unbounded"> + <xsd:element name="metadata"> + <xsd:complexType> + <xsd:sequence> + <xsd:element name="value" type="xsd:string" minOccurs="0" /> + </xsd:sequence> + <xsd:attribute name="name" use="required" type="xsd:string" /> + <xsd:attribute name="type" type="xsd:string" /> + <xsd:attribute name="mimetype" type="xsd:string" /> + <xsd:attribute ref="xml:space" /> + </xsd:complexType> + </xsd:element> + <xsd:element name="assembly"> + <xsd:complexType> + <xsd:attribute name="alias" type="xsd:string" /> + <xsd:attribute name="name" type="xsd:string" /> + </xsd:complexType> + </xsd:element> + <xsd:element name="data"> + <xsd:complexType> + <xsd:sequence> + <xsd:element name="value" type="xsd:string" minOccurs="0" msdata:Ordinal="1" /> + <xsd:element name="comment" type="xsd:string" minOccurs="0" msdata:Ordinal="2" /> + </xsd:sequence> + <xsd:attribute name="name" type="xsd:string" use="required" msdata:Ordinal="1" /> + <xsd:attribute name="type" type="xsd:string" msdata:Ordinal="3" /> + <xsd:attribute name="mimetype" type="xsd:string" msdata:Ordinal="4" /> + <xsd:attribute ref="xml:space" /> + </xsd:complexType> + </xsd:element> + <xsd:element name="resheader"> + <xsd:complexType> + <xsd:sequence> + <xsd:element name="value" type="xsd:string" minOccurs="0" msdata:Ordinal="1" /> + </xsd:sequence> + <xsd:attribute name="name" type="xsd:string" use="required" /> + </xsd:complexType> + </xsd:element> + </xsd:choice> + </xsd:complexType> + </xsd:element> + </xsd:schema> + <resheader name="resmimetype"> + <value>text/microsoft-resx</value> + </resheader> + <resheader name="version"> + <value>2.0</value> + </resheader> + <resheader name="reader"> + <value>System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value> + </resheader> + <resheader name="writer"> + <value>System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value> + </resheader> + <data name="Cancel" xml:space="preserve"> + <value>Zrušiť</value> + <comment>ContextActionCell Cancel button</comment> + </data> + <data name="More" xml:space="preserve"> + <value>Viac</value> + <comment>ContextActionCell More button</comment> + </data> +</root>
\ No newline at end of file diff --git a/Xamarin.Forms.Platform.iOS/Resources/StringResources.sv.resx b/Xamarin.Forms.Platform.iOS/Resources/StringResources.sv.resx new file mode 100644 index 00000000..671f06f6 --- /dev/null +++ b/Xamarin.Forms.Platform.iOS/Resources/StringResources.sv.resx @@ -0,0 +1,128 @@ +<?xml version="1.0" encoding="utf-8"?> +<root> + <!-- + Microsoft ResX Schema + + Version 2.0 + + The primary goals of this format is to allow a simple XML format + that is mostly human readable. The generation and parsing of the + various data types are done through the TypeConverter classes + associated with the data types. + + Example: + + ... ado.net/XML headers & schema ... + <resheader name="resmimetype">text/microsoft-resx</resheader> + <resheader name="version">2.0</resheader> + <resheader name="reader">System.Resources.ResXResourceReader, System.Windows.Forms, ...</resheader> + <resheader name="writer">System.Resources.ResXResourceWriter, System.Windows.Forms, ...</resheader> + <data name="Name1"><value>this is my long string</value><comment>this is a comment</comment></data> + <data name="Color1" type="System.Drawing.Color, System.Drawing">Blue</data> + <data name="Bitmap1" mimetype="application/x-microsoft.net.object.binary.base64"> + <value>[base64 mime encoded serialized .NET Framework object]</value> + </data> + <data name="Icon1" type="System.Drawing.Icon, System.Drawing" mimetype="application/x-microsoft.net.object.bytearray.base64"> + <value>[base64 mime encoded string representing a byte array form of the .NET Framework object]</value> + <comment>This is a comment</comment> + </data> + + There are any number of "resheader" rows that contain simple + name/value pairs. + + Each data row contains a name, and value. The row also contains a + type or mimetype. Type corresponds to a .NET class that support + text/value conversion through the TypeConverter architecture. + Classes that don't support this are serialized and stored with the + mimetype set. + + The mimetype is used for serialized objects, and tells the + ResXResourceReader how to depersist the object. This is currently not + extensible. For a given mimetype the value must be set accordingly: + + Note - application/x-microsoft.net.object.binary.base64 is the format + that the ResXResourceWriter will generate, however the reader can + read any of the formats listed below. + + mimetype: application/x-microsoft.net.object.binary.base64 + value : The object must be serialized with + : System.Runtime.Serialization.Formatters.Binary.BinaryFormatter + : and then encoded with base64 encoding. + + mimetype: application/x-microsoft.net.object.soap.base64 + value : The object must be serialized with + : System.Runtime.Serialization.Formatters.Soap.SoapFormatter + : and then encoded with base64 encoding. + + mimetype: application/x-microsoft.net.object.bytearray.base64 + value : The object must be serialized into a byte array + : using a System.ComponentModel.TypeConverter + : and then encoded with base64 encoding. + --> + <xsd:schema id="root" xmlns="" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:msdata="urn:schemas-microsoft-com:xml-msdata"> + <xsd:import namespace="http://www.w3.org/XML/1998/namespace" /> + <xsd:element name="root" msdata:IsDataSet="true"> + <xsd:complexType> + <xsd:choice maxOccurs="unbounded"> + <xsd:element name="metadata"> + <xsd:complexType> + <xsd:sequence> + <xsd:element name="value" type="xsd:string" minOccurs="0" /> + </xsd:sequence> + <xsd:attribute name="name" use="required" type="xsd:string" /> + <xsd:attribute name="type" type="xsd:string" /> + <xsd:attribute name="mimetype" type="xsd:string" /> + <xsd:attribute ref="xml:space" /> + </xsd:complexType> + </xsd:element> + <xsd:element name="assembly"> + <xsd:complexType> + <xsd:attribute name="alias" type="xsd:string" /> + <xsd:attribute name="name" type="xsd:string" /> + </xsd:complexType> + </xsd:element> + <xsd:element name="data"> + <xsd:complexType> + <xsd:sequence> + <xsd:element name="value" type="xsd:string" minOccurs="0" msdata:Ordinal="1" /> + <xsd:element name="comment" type="xsd:string" minOccurs="0" msdata:Ordinal="2" /> + </xsd:sequence> + <xsd:attribute name="name" type="xsd:string" use="required" msdata:Ordinal="1" /> + <xsd:attribute name="type" type="xsd:string" msdata:Ordinal="3" /> + <xsd:attribute name="mimetype" type="xsd:string" msdata:Ordinal="4" /> + <xsd:attribute ref="xml:space" /> + </xsd:complexType> + </xsd:element> + <xsd:element name="resheader"> + <xsd:complexType> + <xsd:sequence> + <xsd:element name="value" type="xsd:string" minOccurs="0" msdata:Ordinal="1" /> + </xsd:sequence> + <xsd:attribute name="name" type="xsd:string" use="required" /> + </xsd:complexType> + </xsd:element> + </xsd:choice> + </xsd:complexType> + </xsd:element> + </xsd:schema> + <resheader name="resmimetype"> + <value>text/microsoft-resx</value> + </resheader> + <resheader name="version"> + <value>2.0</value> + </resheader> + <resheader name="reader"> + <value>System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value> + </resheader> + <resheader name="writer"> + <value>System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value> + </resheader> + <data name="Cancel" xml:space="preserve"> + <value>Avbryt</value> + <comment>ContextActionCell Cancel button</comment> + </data> + <data name="More" xml:space="preserve"> + <value>Mer</value> + <comment>ContextActionCell More button</comment> + </data> +</root>
\ No newline at end of file diff --git a/Xamarin.Forms.Platform.iOS/Resources/StringResources.th.resx b/Xamarin.Forms.Platform.iOS/Resources/StringResources.th.resx new file mode 100644 index 00000000..b9002921 --- /dev/null +++ b/Xamarin.Forms.Platform.iOS/Resources/StringResources.th.resx @@ -0,0 +1,128 @@ +<?xml version="1.0" encoding="utf-8"?> +<root> + <!-- + Microsoft ResX Schema + + Version 2.0 + + The primary goals of this format is to allow a simple XML format + that is mostly human readable. The generation and parsing of the + various data types are done through the TypeConverter classes + associated with the data types. + + Example: + + ... ado.net/XML headers & schema ... + <resheader name="resmimetype">text/microsoft-resx</resheader> + <resheader name="version">2.0</resheader> + <resheader name="reader">System.Resources.ResXResourceReader, System.Windows.Forms, ...</resheader> + <resheader name="writer">System.Resources.ResXResourceWriter, System.Windows.Forms, ...</resheader> + <data name="Name1"><value>this is my long string</value><comment>this is a comment</comment></data> + <data name="Color1" type="System.Drawing.Color, System.Drawing">Blue</data> + <data name="Bitmap1" mimetype="application/x-microsoft.net.object.binary.base64"> + <value>[base64 mime encoded serialized .NET Framework object]</value> + </data> + <data name="Icon1" type="System.Drawing.Icon, System.Drawing" mimetype="application/x-microsoft.net.object.bytearray.base64"> + <value>[base64 mime encoded string representing a byte array form of the .NET Framework object]</value> + <comment>This is a comment</comment> + </data> + + There are any number of "resheader" rows that contain simple + name/value pairs. + + Each data row contains a name, and value. The row also contains a + type or mimetype. Type corresponds to a .NET class that support + text/value conversion through the TypeConverter architecture. + Classes that don't support this are serialized and stored with the + mimetype set. + + The mimetype is used for serialized objects, and tells the + ResXResourceReader how to depersist the object. This is currently not + extensible. For a given mimetype the value must be set accordingly: + + Note - application/x-microsoft.net.object.binary.base64 is the format + that the ResXResourceWriter will generate, however the reader can + read any of the formats listed below. + + mimetype: application/x-microsoft.net.object.binary.base64 + value : The object must be serialized with + : System.Runtime.Serialization.Formatters.Binary.BinaryFormatter + : and then encoded with base64 encoding. + + mimetype: application/x-microsoft.net.object.soap.base64 + value : The object must be serialized with + : System.Runtime.Serialization.Formatters.Soap.SoapFormatter + : and then encoded with base64 encoding. + + mimetype: application/x-microsoft.net.object.bytearray.base64 + value : The object must be serialized into a byte array + : using a System.ComponentModel.TypeConverter + : and then encoded with base64 encoding. + --> + <xsd:schema id="root" xmlns="" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:msdata="urn:schemas-microsoft-com:xml-msdata"> + <xsd:import namespace="http://www.w3.org/XML/1998/namespace" /> + <xsd:element name="root" msdata:IsDataSet="true"> + <xsd:complexType> + <xsd:choice maxOccurs="unbounded"> + <xsd:element name="metadata"> + <xsd:complexType> + <xsd:sequence> + <xsd:element name="value" type="xsd:string" minOccurs="0" /> + </xsd:sequence> + <xsd:attribute name="name" use="required" type="xsd:string" /> + <xsd:attribute name="type" type="xsd:string" /> + <xsd:attribute name="mimetype" type="xsd:string" /> + <xsd:attribute ref="xml:space" /> + </xsd:complexType> + </xsd:element> + <xsd:element name="assembly"> + <xsd:complexType> + <xsd:attribute name="alias" type="xsd:string" /> + <xsd:attribute name="name" type="xsd:string" /> + </xsd:complexType> + </xsd:element> + <xsd:element name="data"> + <xsd:complexType> + <xsd:sequence> + <xsd:element name="value" type="xsd:string" minOccurs="0" msdata:Ordinal="1" /> + <xsd:element name="comment" type="xsd:string" minOccurs="0" msdata:Ordinal="2" /> + </xsd:sequence> + <xsd:attribute name="name" type="xsd:string" use="required" msdata:Ordinal="1" /> + <xsd:attribute name="type" type="xsd:string" msdata:Ordinal="3" /> + <xsd:attribute name="mimetype" type="xsd:string" msdata:Ordinal="4" /> + <xsd:attribute ref="xml:space" /> + </xsd:complexType> + </xsd:element> + <xsd:element name="resheader"> + <xsd:complexType> + <xsd:sequence> + <xsd:element name="value" type="xsd:string" minOccurs="0" msdata:Ordinal="1" /> + </xsd:sequence> + <xsd:attribute name="name" type="xsd:string" use="required" /> + </xsd:complexType> + </xsd:element> + </xsd:choice> + </xsd:complexType> + </xsd:element> + </xsd:schema> + <resheader name="resmimetype"> + <value>text/microsoft-resx</value> + </resheader> + <resheader name="version"> + <value>2.0</value> + </resheader> + <resheader name="reader"> + <value>System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value> + </resheader> + <resheader name="writer"> + <value>System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value> + </resheader> + <data name="Cancel" xml:space="preserve"> + <value>ยกเลิก</value> + <comment>ContextActionCell Cancel button</comment> + </data> + <data name="More" xml:space="preserve"> + <value>เพิ่มเติม</value> + <comment>ContextActionCell More button</comment> + </data> +</root>
\ No newline at end of file diff --git a/Xamarin.Forms.Platform.iOS/Resources/StringResources.tr.resx b/Xamarin.Forms.Platform.iOS/Resources/StringResources.tr.resx new file mode 100644 index 00000000..256919f4 --- /dev/null +++ b/Xamarin.Forms.Platform.iOS/Resources/StringResources.tr.resx @@ -0,0 +1,128 @@ +<?xml version="1.0" encoding="utf-8"?> +<root> + <!-- + Microsoft ResX Schema + + Version 2.0 + + The primary goals of this format is to allow a simple XML format + that is mostly human readable. The generation and parsing of the + various data types are done through the TypeConverter classes + associated with the data types. + + Example: + + ... ado.net/XML headers & schema ... + <resheader name="resmimetype">text/microsoft-resx</resheader> + <resheader name="version">2.0</resheader> + <resheader name="reader">System.Resources.ResXResourceReader, System.Windows.Forms, ...</resheader> + <resheader name="writer">System.Resources.ResXResourceWriter, System.Windows.Forms, ...</resheader> + <data name="Name1"><value>this is my long string</value><comment>this is a comment</comment></data> + <data name="Color1" type="System.Drawing.Color, System.Drawing">Blue</data> + <data name="Bitmap1" mimetype="application/x-microsoft.net.object.binary.base64"> + <value>[base64 mime encoded serialized .NET Framework object]</value> + </data> + <data name="Icon1" type="System.Drawing.Icon, System.Drawing" mimetype="application/x-microsoft.net.object.bytearray.base64"> + <value>[base64 mime encoded string representing a byte array form of the .NET Framework object]</value> + <comment>This is a comment</comment> + </data> + + There are any number of "resheader" rows that contain simple + name/value pairs. + + Each data row contains a name, and value. The row also contains a + type or mimetype. Type corresponds to a .NET class that support + text/value conversion through the TypeConverter architecture. + Classes that don't support this are serialized and stored with the + mimetype set. + + The mimetype is used for serialized objects, and tells the + ResXResourceReader how to depersist the object. This is currently not + extensible. For a given mimetype the value must be set accordingly: + + Note - application/x-microsoft.net.object.binary.base64 is the format + that the ResXResourceWriter will generate, however the reader can + read any of the formats listed below. + + mimetype: application/x-microsoft.net.object.binary.base64 + value : The object must be serialized with + : System.Runtime.Serialization.Formatters.Binary.BinaryFormatter + : and then encoded with base64 encoding. + + mimetype: application/x-microsoft.net.object.soap.base64 + value : The object must be serialized with + : System.Runtime.Serialization.Formatters.Soap.SoapFormatter + : and then encoded with base64 encoding. + + mimetype: application/x-microsoft.net.object.bytearray.base64 + value : The object must be serialized into a byte array + : using a System.ComponentModel.TypeConverter + : and then encoded with base64 encoding. + --> + <xsd:schema id="root" xmlns="" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:msdata="urn:schemas-microsoft-com:xml-msdata"> + <xsd:import namespace="http://www.w3.org/XML/1998/namespace" /> + <xsd:element name="root" msdata:IsDataSet="true"> + <xsd:complexType> + <xsd:choice maxOccurs="unbounded"> + <xsd:element name="metadata"> + <xsd:complexType> + <xsd:sequence> + <xsd:element name="value" type="xsd:string" minOccurs="0" /> + </xsd:sequence> + <xsd:attribute name="name" use="required" type="xsd:string" /> + <xsd:attribute name="type" type="xsd:string" /> + <xsd:attribute name="mimetype" type="xsd:string" /> + <xsd:attribute ref="xml:space" /> + </xsd:complexType> + </xsd:element> + <xsd:element name="assembly"> + <xsd:complexType> + <xsd:attribute name="alias" type="xsd:string" /> + <xsd:attribute name="name" type="xsd:string" /> + </xsd:complexType> + </xsd:element> + <xsd:element name="data"> + <xsd:complexType> + <xsd:sequence> + <xsd:element name="value" type="xsd:string" minOccurs="0" msdata:Ordinal="1" /> + <xsd:element name="comment" type="xsd:string" minOccurs="0" msdata:Ordinal="2" /> + </xsd:sequence> + <xsd:attribute name="name" type="xsd:string" use="required" msdata:Ordinal="1" /> + <xsd:attribute name="type" type="xsd:string" msdata:Ordinal="3" /> + <xsd:attribute name="mimetype" type="xsd:string" msdata:Ordinal="4" /> + <xsd:attribute ref="xml:space" /> + </xsd:complexType> + </xsd:element> + <xsd:element name="resheader"> + <xsd:complexType> + <xsd:sequence> + <xsd:element name="value" type="xsd:string" minOccurs="0" msdata:Ordinal="1" /> + </xsd:sequence> + <xsd:attribute name="name" type="xsd:string" use="required" /> + </xsd:complexType> + </xsd:element> + </xsd:choice> + </xsd:complexType> + </xsd:element> + </xsd:schema> + <resheader name="resmimetype"> + <value>text/microsoft-resx</value> + </resheader> + <resheader name="version"> + <value>2.0</value> + </resheader> + <resheader name="reader"> + <value>System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value> + </resheader> + <resheader name="writer"> + <value>System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value> + </resheader> + <data name="Cancel" xml:space="preserve"> + <value>Vazgeç</value> + <comment>ContextActionCell Cancel button</comment> + </data> + <data name="More" xml:space="preserve"> + <value>Diğer</value> + <comment>ContextActionCell More button</comment> + </data> +</root>
\ No newline at end of file diff --git a/Xamarin.Forms.Platform.iOS/Resources/StringResources.uk.resx b/Xamarin.Forms.Platform.iOS/Resources/StringResources.uk.resx new file mode 100644 index 00000000..c3a5aa44 --- /dev/null +++ b/Xamarin.Forms.Platform.iOS/Resources/StringResources.uk.resx @@ -0,0 +1,128 @@ +<?xml version="1.0" encoding="utf-8"?> +<root> + <!-- + Microsoft ResX Schema + + Version 2.0 + + The primary goals of this format is to allow a simple XML format + that is mostly human readable. The generation and parsing of the + various data types are done through the TypeConverter classes + associated with the data types. + + Example: + + ... ado.net/XML headers & schema ... + <resheader name="resmimetype">text/microsoft-resx</resheader> + <resheader name="version">2.0</resheader> + <resheader name="reader">System.Resources.ResXResourceReader, System.Windows.Forms, ...</resheader> + <resheader name="writer">System.Resources.ResXResourceWriter, System.Windows.Forms, ...</resheader> + <data name="Name1"><value>this is my long string</value><comment>this is a comment</comment></data> + <data name="Color1" type="System.Drawing.Color, System.Drawing">Blue</data> + <data name="Bitmap1" mimetype="application/x-microsoft.net.object.binary.base64"> + <value>[base64 mime encoded serialized .NET Framework object]</value> + </data> + <data name="Icon1" type="System.Drawing.Icon, System.Drawing" mimetype="application/x-microsoft.net.object.bytearray.base64"> + <value>[base64 mime encoded string representing a byte array form of the .NET Framework object]</value> + <comment>This is a comment</comment> + </data> + + There are any number of "resheader" rows that contain simple + name/value pairs. + + Each data row contains a name, and value. The row also contains a + type or mimetype. Type corresponds to a .NET class that support + text/value conversion through the TypeConverter architecture. + Classes that don't support this are serialized and stored with the + mimetype set. + + The mimetype is used for serialized objects, and tells the + ResXResourceReader how to depersist the object. This is currently not + extensible. For a given mimetype the value must be set accordingly: + + Note - application/x-microsoft.net.object.binary.base64 is the format + that the ResXResourceWriter will generate, however the reader can + read any of the formats listed below. + + mimetype: application/x-microsoft.net.object.binary.base64 + value : The object must be serialized with + : System.Runtime.Serialization.Formatters.Binary.BinaryFormatter + : and then encoded with base64 encoding. + + mimetype: application/x-microsoft.net.object.soap.base64 + value : The object must be serialized with + : System.Runtime.Serialization.Formatters.Soap.SoapFormatter + : and then encoded with base64 encoding. + + mimetype: application/x-microsoft.net.object.bytearray.base64 + value : The object must be serialized into a byte array + : using a System.ComponentModel.TypeConverter + : and then encoded with base64 encoding. + --> + <xsd:schema id="root" xmlns="" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:msdata="urn:schemas-microsoft-com:xml-msdata"> + <xsd:import namespace="http://www.w3.org/XML/1998/namespace" /> + <xsd:element name="root" msdata:IsDataSet="true"> + <xsd:complexType> + <xsd:choice maxOccurs="unbounded"> + <xsd:element name="metadata"> + <xsd:complexType> + <xsd:sequence> + <xsd:element name="value" type="xsd:string" minOccurs="0" /> + </xsd:sequence> + <xsd:attribute name="name" use="required" type="xsd:string" /> + <xsd:attribute name="type" type="xsd:string" /> + <xsd:attribute name="mimetype" type="xsd:string" /> + <xsd:attribute ref="xml:space" /> + </xsd:complexType> + </xsd:element> + <xsd:element name="assembly"> + <xsd:complexType> + <xsd:attribute name="alias" type="xsd:string" /> + <xsd:attribute name="name" type="xsd:string" /> + </xsd:complexType> + </xsd:element> + <xsd:element name="data"> + <xsd:complexType> + <xsd:sequence> + <xsd:element name="value" type="xsd:string" minOccurs="0" msdata:Ordinal="1" /> + <xsd:element name="comment" type="xsd:string" minOccurs="0" msdata:Ordinal="2" /> + </xsd:sequence> + <xsd:attribute name="name" type="xsd:string" use="required" msdata:Ordinal="1" /> + <xsd:attribute name="type" type="xsd:string" msdata:Ordinal="3" /> + <xsd:attribute name="mimetype" type="xsd:string" msdata:Ordinal="4" /> + <xsd:attribute ref="xml:space" /> + </xsd:complexType> + </xsd:element> + <xsd:element name="resheader"> + <xsd:complexType> + <xsd:sequence> + <xsd:element name="value" type="xsd:string" minOccurs="0" msdata:Ordinal="1" /> + </xsd:sequence> + <xsd:attribute name="name" type="xsd:string" use="required" /> + </xsd:complexType> + </xsd:element> + </xsd:choice> + </xsd:complexType> + </xsd:element> + </xsd:schema> + <resheader name="resmimetype"> + <value>text/microsoft-resx</value> + </resheader> + <resheader name="version"> + <value>2.0</value> + </resheader> + <resheader name="reader"> + <value>System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value> + </resheader> + <resheader name="writer"> + <value>System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value> + </resheader> + <data name="Cancel" xml:space="preserve"> + <value>Скасувати</value> + <comment>ContextActionCell Cancel button</comment> + </data> + <data name="More" xml:space="preserve"> + <value>Щe</value> + <comment>ContextActionCell More button</comment> + </data> +</root>
\ No newline at end of file diff --git a/Xamarin.Forms.Platform.iOS/Resources/StringResources.vi.resx b/Xamarin.Forms.Platform.iOS/Resources/StringResources.vi.resx new file mode 100644 index 00000000..f8f88e68 --- /dev/null +++ b/Xamarin.Forms.Platform.iOS/Resources/StringResources.vi.resx @@ -0,0 +1,128 @@ +<?xml version="1.0" encoding="utf-8"?> +<root> + <!-- + Microsoft ResX Schema + + Version 2.0 + + The primary goals of this format is to allow a simple XML format + that is mostly human readable. The generation and parsing of the + various data types are done through the TypeConverter classes + associated with the data types. + + Example: + + ... ado.net/XML headers & schema ... + <resheader name="resmimetype">text/microsoft-resx</resheader> + <resheader name="version">2.0</resheader> + <resheader name="reader">System.Resources.ResXResourceReader, System.Windows.Forms, ...</resheader> + <resheader name="writer">System.Resources.ResXResourceWriter, System.Windows.Forms, ...</resheader> + <data name="Name1"><value>this is my long string</value><comment>this is a comment</comment></data> + <data name="Color1" type="System.Drawing.Color, System.Drawing">Blue</data> + <data name="Bitmap1" mimetype="application/x-microsoft.net.object.binary.base64"> + <value>[base64 mime encoded serialized .NET Framework object]</value> + </data> + <data name="Icon1" type="System.Drawing.Icon, System.Drawing" mimetype="application/x-microsoft.net.object.bytearray.base64"> + <value>[base64 mime encoded string representing a byte array form of the .NET Framework object]</value> + <comment>This is a comment</comment> + </data> + + There are any number of "resheader" rows that contain simple + name/value pairs. + + Each data row contains a name, and value. The row also contains a + type or mimetype. Type corresponds to a .NET class that support + text/value conversion through the TypeConverter architecture. + Classes that don't support this are serialized and stored with the + mimetype set. + + The mimetype is used for serialized objects, and tells the + ResXResourceReader how to depersist the object. This is currently not + extensible. For a given mimetype the value must be set accordingly: + + Note - application/x-microsoft.net.object.binary.base64 is the format + that the ResXResourceWriter will generate, however the reader can + read any of the formats listed below. + + mimetype: application/x-microsoft.net.object.binary.base64 + value : The object must be serialized with + : System.Runtime.Serialization.Formatters.Binary.BinaryFormatter + : and then encoded with base64 encoding. + + mimetype: application/x-microsoft.net.object.soap.base64 + value : The object must be serialized with + : System.Runtime.Serialization.Formatters.Soap.SoapFormatter + : and then encoded with base64 encoding. + + mimetype: application/x-microsoft.net.object.bytearray.base64 + value : The object must be serialized into a byte array + : using a System.ComponentModel.TypeConverter + : and then encoded with base64 encoding. + --> + <xsd:schema id="root" xmlns="" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:msdata="urn:schemas-microsoft-com:xml-msdata"> + <xsd:import namespace="http://www.w3.org/XML/1998/namespace" /> + <xsd:element name="root" msdata:IsDataSet="true"> + <xsd:complexType> + <xsd:choice maxOccurs="unbounded"> + <xsd:element name="metadata"> + <xsd:complexType> + <xsd:sequence> + <xsd:element name="value" type="xsd:string" minOccurs="0" /> + </xsd:sequence> + <xsd:attribute name="name" use="required" type="xsd:string" /> + <xsd:attribute name="type" type="xsd:string" /> + <xsd:attribute name="mimetype" type="xsd:string" /> + <xsd:attribute ref="xml:space" /> + </xsd:complexType> + </xsd:element> + <xsd:element name="assembly"> + <xsd:complexType> + <xsd:attribute name="alias" type="xsd:string" /> + <xsd:attribute name="name" type="xsd:string" /> + </xsd:complexType> + </xsd:element> + <xsd:element name="data"> + <xsd:complexType> + <xsd:sequence> + <xsd:element name="value" type="xsd:string" minOccurs="0" msdata:Ordinal="1" /> + <xsd:element name="comment" type="xsd:string" minOccurs="0" msdata:Ordinal="2" /> + </xsd:sequence> + <xsd:attribute name="name" type="xsd:string" use="required" msdata:Ordinal="1" /> + <xsd:attribute name="type" type="xsd:string" msdata:Ordinal="3" /> + <xsd:attribute name="mimetype" type="xsd:string" msdata:Ordinal="4" /> + <xsd:attribute ref="xml:space" /> + </xsd:complexType> + </xsd:element> + <xsd:element name="resheader"> + <xsd:complexType> + <xsd:sequence> + <xsd:element name="value" type="xsd:string" minOccurs="0" msdata:Ordinal="1" /> + </xsd:sequence> + <xsd:attribute name="name" type="xsd:string" use="required" /> + </xsd:complexType> + </xsd:element> + </xsd:choice> + </xsd:complexType> + </xsd:element> + </xsd:schema> + <resheader name="resmimetype"> + <value>text/microsoft-resx</value> + </resheader> + <resheader name="version"> + <value>2.0</value> + </resheader> + <resheader name="reader"> + <value>System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value> + </resheader> + <resheader name="writer"> + <value>System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value> + </resheader> + <data name="Cancel" xml:space="preserve"> + <value>Hủy</value> + <comment>ContextActionCell Cancel button</comment> + </data> + <data name="More" xml:space="preserve"> + <value>Thêm</value> + <comment>ContextActionCell More button</comment> + </data> +</root>
\ No newline at end of file diff --git a/Xamarin.Forms.Platform.iOS/Resources/StringResources.zh-HK.resx b/Xamarin.Forms.Platform.iOS/Resources/StringResources.zh-HK.resx new file mode 100644 index 00000000..fff96457 --- /dev/null +++ b/Xamarin.Forms.Platform.iOS/Resources/StringResources.zh-HK.resx @@ -0,0 +1,128 @@ +<?xml version="1.0" encoding="utf-8"?> +<root> + <!-- + Microsoft ResX Schema + + Version 2.0 + + The primary goals of this format is to allow a simple XML format + that is mostly human readable. The generation and parsing of the + various data types are done through the TypeConverter classes + associated with the data types. + + Example: + + ... ado.net/XML headers & schema ... + <resheader name="resmimetype">text/microsoft-resx</resheader> + <resheader name="version">2.0</resheader> + <resheader name="reader">System.Resources.ResXResourceReader, System.Windows.Forms, ...</resheader> + <resheader name="writer">System.Resources.ResXResourceWriter, System.Windows.Forms, ...</resheader> + <data name="Name1"><value>this is my long string</value><comment>this is a comment</comment></data> + <data name="Color1" type="System.Drawing.Color, System.Drawing">Blue</data> + <data name="Bitmap1" mimetype="application/x-microsoft.net.object.binary.base64"> + <value>[base64 mime encoded serialized .NET Framework object]</value> + </data> + <data name="Icon1" type="System.Drawing.Icon, System.Drawing" mimetype="application/x-microsoft.net.object.bytearray.base64"> + <value>[base64 mime encoded string representing a byte array form of the .NET Framework object]</value> + <comment>This is a comment</comment> + </data> + + There are any number of "resheader" rows that contain simple + name/value pairs. + + Each data row contains a name, and value. The row also contains a + type or mimetype. Type corresponds to a .NET class that support + text/value conversion through the TypeConverter architecture. + Classes that don't support this are serialized and stored with the + mimetype set. + + The mimetype is used for serialized objects, and tells the + ResXResourceReader how to depersist the object. This is currently not + extensible. For a given mimetype the value must be set accordingly: + + Note - application/x-microsoft.net.object.binary.base64 is the format + that the ResXResourceWriter will generate, however the reader can + read any of the formats listed below. + + mimetype: application/x-microsoft.net.object.binary.base64 + value : The object must be serialized with + : System.Runtime.Serialization.Formatters.Binary.BinaryFormatter + : and then encoded with base64 encoding. + + mimetype: application/x-microsoft.net.object.soap.base64 + value : The object must be serialized with + : System.Runtime.Serialization.Formatters.Soap.SoapFormatter + : and then encoded with base64 encoding. + + mimetype: application/x-microsoft.net.object.bytearray.base64 + value : The object must be serialized into a byte array + : using a System.ComponentModel.TypeConverter + : and then encoded with base64 encoding. + --> + <xsd:schema id="root" xmlns="" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:msdata="urn:schemas-microsoft-com:xml-msdata"> + <xsd:import namespace="http://www.w3.org/XML/1998/namespace" /> + <xsd:element name="root" msdata:IsDataSet="true"> + <xsd:complexType> + <xsd:choice maxOccurs="unbounded"> + <xsd:element name="metadata"> + <xsd:complexType> + <xsd:sequence> + <xsd:element name="value" type="xsd:string" minOccurs="0" /> + </xsd:sequence> + <xsd:attribute name="name" use="required" type="xsd:string" /> + <xsd:attribute name="type" type="xsd:string" /> + <xsd:attribute name="mimetype" type="xsd:string" /> + <xsd:attribute ref="xml:space" /> + </xsd:complexType> + </xsd:element> + <xsd:element name="assembly"> + <xsd:complexType> + <xsd:attribute name="alias" type="xsd:string" /> + <xsd:attribute name="name" type="xsd:string" /> + </xsd:complexType> + </xsd:element> + <xsd:element name="data"> + <xsd:complexType> + <xsd:sequence> + <xsd:element name="value" type="xsd:string" minOccurs="0" msdata:Ordinal="1" /> + <xsd:element name="comment" type="xsd:string" minOccurs="0" msdata:Ordinal="2" /> + </xsd:sequence> + <xsd:attribute name="name" type="xsd:string" use="required" msdata:Ordinal="1" /> + <xsd:attribute name="type" type="xsd:string" msdata:Ordinal="3" /> + <xsd:attribute name="mimetype" type="xsd:string" msdata:Ordinal="4" /> + <xsd:attribute ref="xml:space" /> + </xsd:complexType> + </xsd:element> + <xsd:element name="resheader"> + <xsd:complexType> + <xsd:sequence> + <xsd:element name="value" type="xsd:string" minOccurs="0" msdata:Ordinal="1" /> + </xsd:sequence> + <xsd:attribute name="name" type="xsd:string" use="required" /> + </xsd:complexType> + </xsd:element> + </xsd:choice> + </xsd:complexType> + </xsd:element> + </xsd:schema> + <resheader name="resmimetype"> + <value>text/microsoft-resx</value> + </resheader> + <resheader name="version"> + <value>2.0</value> + </resheader> + <resheader name="reader"> + <value>System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value> + </resheader> + <resheader name="writer"> + <value>System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value> + </resheader> + <data name="Cancel" xml:space="preserve"> + <value>取消</value> + <comment>ContextActionCell Cancel button</comment> + </data> + <data name="More" xml:space="preserve"> + <value>更多</value> + <comment>ContextActionCell More button</comment> + </data> +</root>
\ No newline at end of file diff --git a/Xamarin.Forms.Platform.iOS/Resources/StringResources.zh-Hans.resx b/Xamarin.Forms.Platform.iOS/Resources/StringResources.zh-Hans.resx new file mode 100644 index 00000000..fff96457 --- /dev/null +++ b/Xamarin.Forms.Platform.iOS/Resources/StringResources.zh-Hans.resx @@ -0,0 +1,128 @@ +<?xml version="1.0" encoding="utf-8"?> +<root> + <!-- + Microsoft ResX Schema + + Version 2.0 + + The primary goals of this format is to allow a simple XML format + that is mostly human readable. The generation and parsing of the + various data types are done through the TypeConverter classes + associated with the data types. + + Example: + + ... ado.net/XML headers & schema ... + <resheader name="resmimetype">text/microsoft-resx</resheader> + <resheader name="version">2.0</resheader> + <resheader name="reader">System.Resources.ResXResourceReader, System.Windows.Forms, ...</resheader> + <resheader name="writer">System.Resources.ResXResourceWriter, System.Windows.Forms, ...</resheader> + <data name="Name1"><value>this is my long string</value><comment>this is a comment</comment></data> + <data name="Color1" type="System.Drawing.Color, System.Drawing">Blue</data> + <data name="Bitmap1" mimetype="application/x-microsoft.net.object.binary.base64"> + <value>[base64 mime encoded serialized .NET Framework object]</value> + </data> + <data name="Icon1" type="System.Drawing.Icon, System.Drawing" mimetype="application/x-microsoft.net.object.bytearray.base64"> + <value>[base64 mime encoded string representing a byte array form of the .NET Framework object]</value> + <comment>This is a comment</comment> + </data> + + There are any number of "resheader" rows that contain simple + name/value pairs. + + Each data row contains a name, and value. The row also contains a + type or mimetype. Type corresponds to a .NET class that support + text/value conversion through the TypeConverter architecture. + Classes that don't support this are serialized and stored with the + mimetype set. + + The mimetype is used for serialized objects, and tells the + ResXResourceReader how to depersist the object. This is currently not + extensible. For a given mimetype the value must be set accordingly: + + Note - application/x-microsoft.net.object.binary.base64 is the format + that the ResXResourceWriter will generate, however the reader can + read any of the formats listed below. + + mimetype: application/x-microsoft.net.object.binary.base64 + value : The object must be serialized with + : System.Runtime.Serialization.Formatters.Binary.BinaryFormatter + : and then encoded with base64 encoding. + + mimetype: application/x-microsoft.net.object.soap.base64 + value : The object must be serialized with + : System.Runtime.Serialization.Formatters.Soap.SoapFormatter + : and then encoded with base64 encoding. + + mimetype: application/x-microsoft.net.object.bytearray.base64 + value : The object must be serialized into a byte array + : using a System.ComponentModel.TypeConverter + : and then encoded with base64 encoding. + --> + <xsd:schema id="root" xmlns="" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:msdata="urn:schemas-microsoft-com:xml-msdata"> + <xsd:import namespace="http://www.w3.org/XML/1998/namespace" /> + <xsd:element name="root" msdata:IsDataSet="true"> + <xsd:complexType> + <xsd:choice maxOccurs="unbounded"> + <xsd:element name="metadata"> + <xsd:complexType> + <xsd:sequence> + <xsd:element name="value" type="xsd:string" minOccurs="0" /> + </xsd:sequence> + <xsd:attribute name="name" use="required" type="xsd:string" /> + <xsd:attribute name="type" type="xsd:string" /> + <xsd:attribute name="mimetype" type="xsd:string" /> + <xsd:attribute ref="xml:space" /> + </xsd:complexType> + </xsd:element> + <xsd:element name="assembly"> + <xsd:complexType> + <xsd:attribute name="alias" type="xsd:string" /> + <xsd:attribute name="name" type="xsd:string" /> + </xsd:complexType> + </xsd:element> + <xsd:element name="data"> + <xsd:complexType> + <xsd:sequence> + <xsd:element name="value" type="xsd:string" minOccurs="0" msdata:Ordinal="1" /> + <xsd:element name="comment" type="xsd:string" minOccurs="0" msdata:Ordinal="2" /> + </xsd:sequence> + <xsd:attribute name="name" type="xsd:string" use="required" msdata:Ordinal="1" /> + <xsd:attribute name="type" type="xsd:string" msdata:Ordinal="3" /> + <xsd:attribute name="mimetype" type="xsd:string" msdata:Ordinal="4" /> + <xsd:attribute ref="xml:space" /> + </xsd:complexType> + </xsd:element> + <xsd:element name="resheader"> + <xsd:complexType> + <xsd:sequence> + <xsd:element name="value" type="xsd:string" minOccurs="0" msdata:Ordinal="1" /> + </xsd:sequence> + <xsd:attribute name="name" type="xsd:string" use="required" /> + </xsd:complexType> + </xsd:element> + </xsd:choice> + </xsd:complexType> + </xsd:element> + </xsd:schema> + <resheader name="resmimetype"> + <value>text/microsoft-resx</value> + </resheader> + <resheader name="version"> + <value>2.0</value> + </resheader> + <resheader name="reader"> + <value>System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value> + </resheader> + <resheader name="writer"> + <value>System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value> + </resheader> + <data name="Cancel" xml:space="preserve"> + <value>取消</value> + <comment>ContextActionCell Cancel button</comment> + </data> + <data name="More" xml:space="preserve"> + <value>更多</value> + <comment>ContextActionCell More button</comment> + </data> +</root>
\ No newline at end of file diff --git a/Xamarin.Forms.Platform.iOS/Resources/StringResources.zh-Hant.resx b/Xamarin.Forms.Platform.iOS/Resources/StringResources.zh-Hant.resx new file mode 100644 index 00000000..fff96457 --- /dev/null +++ b/Xamarin.Forms.Platform.iOS/Resources/StringResources.zh-Hant.resx @@ -0,0 +1,128 @@ +<?xml version="1.0" encoding="utf-8"?> +<root> + <!-- + Microsoft ResX Schema + + Version 2.0 + + The primary goals of this format is to allow a simple XML format + that is mostly human readable. The generation and parsing of the + various data types are done through the TypeConverter classes + associated with the data types. + + Example: + + ... ado.net/XML headers & schema ... + <resheader name="resmimetype">text/microsoft-resx</resheader> + <resheader name="version">2.0</resheader> + <resheader name="reader">System.Resources.ResXResourceReader, System.Windows.Forms, ...</resheader> + <resheader name="writer">System.Resources.ResXResourceWriter, System.Windows.Forms, ...</resheader> + <data name="Name1"><value>this is my long string</value><comment>this is a comment</comment></data> + <data name="Color1" type="System.Drawing.Color, System.Drawing">Blue</data> + <data name="Bitmap1" mimetype="application/x-microsoft.net.object.binary.base64"> + <value>[base64 mime encoded serialized .NET Framework object]</value> + </data> + <data name="Icon1" type="System.Drawing.Icon, System.Drawing" mimetype="application/x-microsoft.net.object.bytearray.base64"> + <value>[base64 mime encoded string representing a byte array form of the .NET Framework object]</value> + <comment>This is a comment</comment> + </data> + + There are any number of "resheader" rows that contain simple + name/value pairs. + + Each data row contains a name, and value. The row also contains a + type or mimetype. Type corresponds to a .NET class that support + text/value conversion through the TypeConverter architecture. + Classes that don't support this are serialized and stored with the + mimetype set. + + The mimetype is used for serialized objects, and tells the + ResXResourceReader how to depersist the object. This is currently not + extensible. For a given mimetype the value must be set accordingly: + + Note - application/x-microsoft.net.object.binary.base64 is the format + that the ResXResourceWriter will generate, however the reader can + read any of the formats listed below. + + mimetype: application/x-microsoft.net.object.binary.base64 + value : The object must be serialized with + : System.Runtime.Serialization.Formatters.Binary.BinaryFormatter + : and then encoded with base64 encoding. + + mimetype: application/x-microsoft.net.object.soap.base64 + value : The object must be serialized with + : System.Runtime.Serialization.Formatters.Soap.SoapFormatter + : and then encoded with base64 encoding. + + mimetype: application/x-microsoft.net.object.bytearray.base64 + value : The object must be serialized into a byte array + : using a System.ComponentModel.TypeConverter + : and then encoded with base64 encoding. + --> + <xsd:schema id="root" xmlns="" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:msdata="urn:schemas-microsoft-com:xml-msdata"> + <xsd:import namespace="http://www.w3.org/XML/1998/namespace" /> + <xsd:element name="root" msdata:IsDataSet="true"> + <xsd:complexType> + <xsd:choice maxOccurs="unbounded"> + <xsd:element name="metadata"> + <xsd:complexType> + <xsd:sequence> + <xsd:element name="value" type="xsd:string" minOccurs="0" /> + </xsd:sequence> + <xsd:attribute name="name" use="required" type="xsd:string" /> + <xsd:attribute name="type" type="xsd:string" /> + <xsd:attribute name="mimetype" type="xsd:string" /> + <xsd:attribute ref="xml:space" /> + </xsd:complexType> + </xsd:element> + <xsd:element name="assembly"> + <xsd:complexType> + <xsd:attribute name="alias" type="xsd:string" /> + <xsd:attribute name="name" type="xsd:string" /> + </xsd:complexType> + </xsd:element> + <xsd:element name="data"> + <xsd:complexType> + <xsd:sequence> + <xsd:element name="value" type="xsd:string" minOccurs="0" msdata:Ordinal="1" /> + <xsd:element name="comment" type="xsd:string" minOccurs="0" msdata:Ordinal="2" /> + </xsd:sequence> + <xsd:attribute name="name" type="xsd:string" use="required" msdata:Ordinal="1" /> + <xsd:attribute name="type" type="xsd:string" msdata:Ordinal="3" /> + <xsd:attribute name="mimetype" type="xsd:string" msdata:Ordinal="4" /> + <xsd:attribute ref="xml:space" /> + </xsd:complexType> + </xsd:element> + <xsd:element name="resheader"> + <xsd:complexType> + <xsd:sequence> + <xsd:element name="value" type="xsd:string" minOccurs="0" msdata:Ordinal="1" /> + </xsd:sequence> + <xsd:attribute name="name" type="xsd:string" use="required" /> + </xsd:complexType> + </xsd:element> + </xsd:choice> + </xsd:complexType> + </xsd:element> + </xsd:schema> + <resheader name="resmimetype"> + <value>text/microsoft-resx</value> + </resheader> + <resheader name="version"> + <value>2.0</value> + </resheader> + <resheader name="reader"> + <value>System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value> + </resheader> + <resheader name="writer"> + <value>System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value> + </resheader> + <data name="Cancel" xml:space="preserve"> + <value>取消</value> + <comment>ContextActionCell Cancel button</comment> + </data> + <data name="More" xml:space="preserve"> + <value>更多</value> + <comment>ContextActionCell More button</comment> + </data> +</root>
\ No newline at end of file diff --git a/Xamarin.Forms.Platform.iOS/ResourcesProvider.cs b/Xamarin.Forms.Platform.iOS/ResourcesProvider.cs new file mode 100644 index 00000000..847107d9 --- /dev/null +++ b/Xamarin.Forms.Platform.iOS/ResourcesProvider.cs @@ -0,0 +1,72 @@ +#if __UNIFIED__ +using UIKit; + +#else +using MonoTouch.UIKit; +#endif + +namespace Xamarin.Forms.Platform.iOS +{ + internal class ResourcesProvider : ISystemResourcesProvider + { + ResourceDictionary _dictionary; + + public ResourcesProvider() + { + if (Forms.IsiOS7OrNewer) + UIApplication.Notifications.ObserveContentSizeCategoryChanged((sender, args) => UpdateStyles()); + } + + public IResourceDictionary GetSystemResources() + { + _dictionary = new ResourceDictionary(); + UpdateStyles(); + + return _dictionary; + } + + Style GenerateListItemDetailTextStyle() + { + var font = new UITableViewCell(UITableViewCellStyle.Subtitle, "Foobar").DetailTextLabel.Font; + return GenerateStyle(font); + } + + Style GenerateListItemTextStyle() + { + var font = new UITableViewCell(UITableViewCellStyle.Subtitle, "Foobar").TextLabel.Font; + return GenerateStyle(font); + } + + Style GenerateStyle(UIFont font) + { + var result = new Style(typeof(Label)); + + result.Setters.Add(new Setter { Property = Label.FontSizeProperty, Value = (double)font.PointSize }); + + result.Setters.Add(new Setter { Property = Label.FontFamilyProperty, Value = font.Name }); + + return result; + } + + void UpdateStyles() + { + if (Forms.IsiOS7OrNewer) + { + _dictionary[Device.Styles.TitleStyleKey] = GenerateStyle(UIFont.PreferredHeadline); + _dictionary[Device.Styles.SubtitleStyleKey] = GenerateStyle(UIFont.PreferredSubheadline); + _dictionary[Device.Styles.BodyStyleKey] = GenerateStyle(UIFont.PreferredBody); + _dictionary[Device.Styles.CaptionStyleKey] = GenerateStyle(UIFont.PreferredCaption1); + } + else + { + _dictionary[Device.Styles.TitleStyleKey] = GenerateStyle(UIFont.BoldSystemFontOfSize(17)); + _dictionary[Device.Styles.SubtitleStyleKey] = GenerateStyle(UIFont.SystemFontOfSize(15)); + _dictionary[Device.Styles.BodyStyleKey] = GenerateStyle(UIFont.SystemFontOfSize(17)); + _dictionary[Device.Styles.CaptionStyleKey] = GenerateStyle(UIFont.SystemFontOfSize(12)); + } + + _dictionary[Device.Styles.ListItemTextStyleKey] = GenerateListItemTextStyle(); + _dictionary[Device.Styles.ListItemDetailTextStyleKey] = GenerateListItemDetailTextStyle(); + } + } +}
\ No newline at end of file diff --git a/Xamarin.Forms.Platform.iOS/ViewInitializedEventArgs.cs b/Xamarin.Forms.Platform.iOS/ViewInitializedEventArgs.cs new file mode 100644 index 00000000..e34a493e --- /dev/null +++ b/Xamarin.Forms.Platform.iOS/ViewInitializedEventArgs.cs @@ -0,0 +1,17 @@ +using System; +#if __UNIFIED__ +using UIKit; + +#else +using MonoTouch.UIKit; +#endif + +namespace Xamarin.Forms +{ + public class ViewInitializedEventArgs : EventArgs + { + public UIView NativeView { get; internal set; } + + public VisualElement View { get; internal set; } + } +}
\ No newline at end of file diff --git a/Xamarin.Forms.Platform.iOS/ViewRenderer.cs b/Xamarin.Forms.Platform.iOS/ViewRenderer.cs new file mode 100644 index 00000000..5b3be1c3 --- /dev/null +++ b/Xamarin.Forms.Platform.iOS/ViewRenderer.cs @@ -0,0 +1,151 @@ +using System; +using System.Drawing; +using System.ComponentModel; +#if __UNIFIED__ +using UIKit; +#else +using MonoTouch.UIKit; +#endif +#if __UNIFIED__ +using RectangleF = CoreGraphics.CGRect; +using SizeF = CoreGraphics.CGSize; +using PointF = CoreGraphics.CGPoint; + +#else +using nfloat=System.Single; +using nint=System.Int32; +using nuint=System.UInt32; +#endif + +namespace Xamarin.Forms.Platform.iOS +{ + public abstract class ViewRenderer : ViewRenderer<View, UIView> + { + } + + public abstract class ViewRenderer<TView, TNativeView> : VisualElementRenderer<TView> where TView : View where TNativeView : UIView + { + UIColor _defaultColor; + + public TNativeView Control { get; private set; } + + public override void LayoutSubviews() + { + base.LayoutSubviews(); + if (Control != null) + Control.Frame = new RectangleF(0, 0, (nfloat)Element.Width, (nfloat)Element.Height); + } + + public override SizeF SizeThatFits(SizeF size) + { + return Control.SizeThatFits(size); + } + + protected override void Dispose(bool disposing) + { + base.Dispose(disposing); + + if (disposing && Control != null) + { + Control.Dispose(); + Control = null; + } + } + + protected override void OnElementChanged(ElementChangedEventArgs<TView> e) + { + base.OnElementChanged(e); + + if (e.OldElement != null) + e.OldElement.FocusChangeRequested -= ViewOnFocusChangeRequested; + + if (e.NewElement != null) + { + if (Control != null && e.OldElement != null && e.OldElement.BackgroundColor != e.NewElement.BackgroundColor || e.NewElement.BackgroundColor != Color.Default) + SetBackgroundColor(e.NewElement.BackgroundColor); + + e.NewElement.FocusChangeRequested += ViewOnFocusChangeRequested; + } + + UpdateIsEnabled(); + } + + protected override void OnElementPropertyChanged(object sender, PropertyChangedEventArgs e) + { + if (Control != null) + { + if (e.PropertyName == VisualElement.IsEnabledProperty.PropertyName) + UpdateIsEnabled(); + else if (e.PropertyName == VisualElement.BackgroundColorProperty.PropertyName) + SetBackgroundColor(Element.BackgroundColor); + } + + base.OnElementPropertyChanged(sender, e); + } + + protected override void OnRegisterEffect(PlatformEffect effect) + { + base.OnRegisterEffect(effect); + effect.Control = Control; + } + + protected override void SetAutomationId(string id) + { + if (Control == null) + base.SetAutomationId(id); + else + { + AccessibilityIdentifier = id + "_Container"; + Control.AccessibilityIdentifier = id; + } + } + + protected override void SetBackgroundColor(Color color) + { + if (Control == null) + return; + + if (color == Color.Default) + Control.BackgroundColor = _defaultColor; + else + Control.BackgroundColor = color.ToUIColor(); + } + + protected void SetNativeControl(TNativeView uiview) + { + _defaultColor = uiview.BackgroundColor; + Control = uiview; + + if (Element.BackgroundColor != Color.Default) + SetBackgroundColor(Element.BackgroundColor); + + UpdateIsEnabled(); + + AddSubview(uiview); + } + + internal override void SendVisualElementInitialized(VisualElement element, UIView nativeView) + { + base.SendVisualElementInitialized(element, Control); + } + + void UpdateIsEnabled() + { + if (Element == null || Control == null) + return; + + var uiControl = Control as UIControl; + if (uiControl == null) + return; + uiControl.Enabled = Element.IsEnabled; + } + + void ViewOnFocusChangeRequested(object sender, VisualElement.FocusRequestArgs focusRequestArgs) + { + if (Control == null) + return; + + focusRequestArgs.Result = focusRequestArgs.Focus ? Control.BecomeFirstResponder() : Control.ResignFirstResponder(); + } + } +}
\ No newline at end of file diff --git a/Xamarin.Forms.Platform.iOS/VisualElementPackager.cs b/Xamarin.Forms.Platform.iOS/VisualElementPackager.cs new file mode 100644 index 00000000..7f511d9d --- /dev/null +++ b/Xamarin.Forms.Platform.iOS/VisualElementPackager.cs @@ -0,0 +1,179 @@ +using System; +#if __UNIFIED__ +using UIKit; + +#else +using MonoTouch.UIKit; +#endif + +namespace Xamarin.Forms.Platform.iOS +{ + public class VisualElementPackager : IDisposable + { + VisualElement _element; + + bool _isDisposed; + + public VisualElementPackager(IVisualElementRenderer renderer) + { + if (renderer == null) + throw new ArgumentNullException("renderer"); + + Renderer = renderer; + renderer.ElementChanged += OnRendererElementChanged; + SetElement(null, renderer.Element); + } + + protected IVisualElementRenderer Renderer { get; set; } + + public void Dispose() + { + Dispose(true); + } + + public void Load() + { + for (var i = 0; i < Renderer.Element.LogicalChildren.Count; i++) + { + var child = Renderer.Element.LogicalChildren[i] as VisualElement; + if (child != null) + OnChildAdded(child); + } + } + + protected virtual void Dispose(bool disposing) + { + if (_isDisposed) + return; + + if (disposing) + { + SetElement(_element, null); + if (Renderer != null) + { + Renderer.ElementChanged -= OnRendererElementChanged; + Renderer = null; + } + } + + _isDisposed = true; + } + + protected virtual void OnChildAdded(VisualElement view) + { + if (_isDisposed) + return; + + var viewRenderer = Platform.CreateRenderer(view); + Platform.SetRenderer(view, viewRenderer); + + var uiview = Renderer.NativeView; + uiview.AddSubview(viewRenderer.NativeView); + + if (Renderer.ViewController != null && viewRenderer.ViewController != null) + Renderer.ViewController.AddChildViewController(viewRenderer.ViewController); + + EnsureChildrenOrder(); + } + + protected virtual void OnChildRemoved(VisualElement view) + { + var viewRenderer = Platform.GetRenderer(view); + if (viewRenderer == null || viewRenderer.NativeView == null) + return; + + viewRenderer.NativeView.RemoveFromSuperview(); + + if (Renderer.ViewController != null && viewRenderer.ViewController != null) + viewRenderer.ViewController.RemoveFromParentViewController(); + } + + void EnsureChildrenOrder() + { + if (Renderer.Element.LogicalChildren.Count == 0) + return; + + for (var z = 0; z < Renderer.Element.LogicalChildren.Count; z++) + { + var child = Renderer.Element.LogicalChildren[z] as VisualElement; + if (child == null) + continue; + var childRenderer = Platform.GetRenderer(child); + + if (childRenderer == null) + continue; + + var nativeControl = childRenderer.NativeView; + + Renderer.NativeView.BringSubviewToFront(nativeControl); + nativeControl.Layer.ZPosition = z * 1000; + } + } + + void OnChildAdded(object sender, ElementEventArgs e) + { + var view = e.Element as VisualElement; + if (view != null) + OnChildAdded(view); + } + + void OnChildRemoved(object sender, ElementEventArgs e) + { + var view = e.Element as VisualElement; + if (view != null) + OnChildRemoved(view); + } + + void OnRendererElementChanged(object sender, VisualElementChangedEventArgs args) + { + if (args.NewElement == _element) + return; + + SetElement(_element, args.NewElement); + } + + void SetElement(VisualElement oldElement, VisualElement newElement) + { + if (oldElement == newElement) + return; + + if (oldElement != null) + { + oldElement.ChildAdded -= OnChildAdded; + oldElement.ChildRemoved -= OnChildRemoved; + oldElement.ChildrenReordered -= UpdateChildrenOrder; + + if (newElement != null) + { + var pool = new RendererPool(Renderer, oldElement); + pool.UpdateNewElement(newElement); + + EnsureChildrenOrder(); + } + else + { + for (var i = 0; i < oldElement.LogicalChildren.Count; i++) + { + var child = oldElement.LogicalChildren[i] as VisualElement; + if (child != null) + OnChildRemoved(child); + } + } + } + + _element = newElement; + + if (newElement != null) + { + newElement.ChildAdded += OnChildAdded; + newElement.ChildRemoved += OnChildRemoved; + newElement.ChildrenReordered += UpdateChildrenOrder; + } + } + + void UpdateChildrenOrder(object sender, EventArgs e) + { + EnsureChildrenOrder(); + } + } +}
\ No newline at end of file diff --git a/Xamarin.Forms.Platform.iOS/VisualElementRenderer.cs b/Xamarin.Forms.Platform.iOS/VisualElementRenderer.cs new file mode 100644 index 00000000..d14c288c --- /dev/null +++ b/Xamarin.Forms.Platform.iOS/VisualElementRenderer.cs @@ -0,0 +1,273 @@ +using System; +using System.Collections.Generic; +using System.Collections.ObjectModel; +using System.Collections.Specialized; +using System.Drawing; +using System.ComponentModel; +#if __UNIFIED__ +using UIKit; +#else +using MonoTouch.UIKit; +#endif +#if __UNIFIED__ +using RectangleF = CoreGraphics.CGRect; +using SizeF = CoreGraphics.CGSize; +using PointF = CoreGraphics.CGPoint; + +#else +using nfloat=System.Single; +using nint=System.Int32; +using nuint=System.UInt32; +#endif + +namespace Xamarin.Forms.Platform.iOS +{ + [Flags] + public enum VisualElementRendererFlags + { + Disposed = 1 << 0, + AutoTrack = 1 << 1, + AutoPackage = 1 << 2 + } + + public class VisualElementRenderer<TElement> : UIView, IVisualElementRenderer, IEffectControlProvider where TElement : VisualElement + { + readonly UIColor _defaultColor = UIColor.Clear; + + readonly List<EventHandler<VisualElementChangedEventArgs>> _elementChangedHandlers = new List<EventHandler<VisualElementChangedEventArgs>>(); + + readonly PropertyChangedEventHandler _propertyChangedHandler; + EventTracker _events; + + VisualElementRendererFlags _flags = VisualElementRendererFlags.AutoPackage | VisualElementRendererFlags.AutoTrack; + + VisualElementPackager _packager; + VisualElementTracker _tracker; + + protected VisualElementRenderer() : base(RectangleF.Empty) + { + _propertyChangedHandler = OnElementPropertyChanged; + BackgroundColor = UIColor.Clear; + } + + // prevent possible crashes in overrides + public sealed override UIColor BackgroundColor + { + get { return base.BackgroundColor; } + set { base.BackgroundColor = value; } + } + + public TElement Element { get; private set; } + + protected bool AutoPackage + { + get { return (_flags & VisualElementRendererFlags.AutoPackage) != 0; } + set + { + if (value) + _flags |= VisualElementRendererFlags.AutoPackage; + else + _flags &= ~VisualElementRendererFlags.AutoPackage; + } + } + + protected bool AutoTrack + { + get { return (_flags & VisualElementRendererFlags.AutoTrack) != 0; } + set + { + if (value) + _flags |= VisualElementRendererFlags.AutoTrack; + else + _flags &= ~VisualElementRendererFlags.AutoTrack; + } + } + + void IEffectControlProvider.RegisterEffect(Effect effect) + { + var platformEffect = effect as PlatformEffect; + if (platformEffect != null) + OnRegisterEffect(platformEffect); + } + + VisualElement IVisualElementRenderer.Element + { + get { return Element; } + } + + event EventHandler<VisualElementChangedEventArgs> IVisualElementRenderer.ElementChanged + { + add { _elementChangedHandlers.Add(value); } + remove { _elementChangedHandlers.Remove(value); } + } + + public virtual SizeRequest GetDesiredSize(double widthConstraint, double heightConstraint) + { + return NativeView.GetSizeRequest(widthConstraint, heightConstraint); + } + + public UIView NativeView + { + get { return this; } + } + + void IVisualElementRenderer.SetElement(VisualElement element) + { + SetElement((TElement)element); + } + + public void SetElementSize(Size size) + { + Layout.LayoutChildIntoBoundingRegion(Element, new Rectangle(Element.X, Element.Y, size.Width, size.Height)); + } + + public UIViewController ViewController + { + get { return null; } + } + + public event EventHandler<ElementChangedEventArgs<TElement>> ElementChanged; + + public void SetElement(TElement element) + { + var oldElement = Element; + Element = element; + + if (oldElement != null) + oldElement.PropertyChanged -= _propertyChangedHandler; + + if (element != null) + { + if (element.BackgroundColor != Color.Default || (oldElement != null && element.BackgroundColor != oldElement.BackgroundColor)) + SetBackgroundColor(element.BackgroundColor); + + UpdateClipToBounds(); + + if (_tracker == null) + { + _tracker = new VisualElementTracker(this); + _tracker.NativeControlUpdated += (sender, e) => UpdateNativeWidget(); + } + + if (AutoPackage && _packager == null) + { + _packager = new VisualElementPackager(this); + _packager.Load(); + } + + if (AutoTrack && _events == null) + { + _events = new EventTracker(this); + _events.LoadEvents(this); + } + + element.PropertyChanged += _propertyChangedHandler; + } + + OnElementChanged(new ElementChangedEventArgs<TElement>(oldElement, element)); + + if (element != null) + SendVisualElementInitialized(element, this); + + var controller = (IElementController)oldElement; + if (controller != null && controller.EffectControlProvider == this) + controller.EffectControlProvider = null; + + controller = element; + if (controller != null) + controller.EffectControlProvider = this; + + if (Element != null && !string.IsNullOrEmpty(Element.AutomationId)) + SetAutomationId(Element.AutomationId); + } + + public override SizeF SizeThatFits(SizeF size) + { + return new SizeF(0, 0); + } + + protected override void Dispose(bool disposing) + { + if ((_flags & VisualElementRendererFlags.Disposed) != 0) + return; + _flags |= VisualElementRendererFlags.Disposed; + + if (disposing) + { + if (_events != null) + { + _events.Dispose(); + _events = null; + } + if (_tracker != null) + { + _tracker.Dispose(); + _tracker = null; + } + if (_packager != null) + { + _packager.Dispose(); + _packager = null; + } + + Platform.SetRenderer(Element, null); + SetElement(null); + Element = null; + } + base.Dispose(disposing); + } + + protected virtual void OnElementChanged(ElementChangedEventArgs<TElement> e) + { + var args = new VisualElementChangedEventArgs(e.OldElement, e.NewElement); + for (var i = 0; i < _elementChangedHandlers.Count; i++) + _elementChangedHandlers[i](this, args); + + var changed = ElementChanged; + if (changed != null) + changed(this, e); + } + + protected virtual void OnElementPropertyChanged(object sender, PropertyChangedEventArgs e) + { + if (e.PropertyName == VisualElement.BackgroundColorProperty.PropertyName) + SetBackgroundColor(Element.BackgroundColor); + else if (e.PropertyName == Layout.IsClippedToBoundsProperty.PropertyName) + UpdateClipToBounds(); + } + + protected virtual void OnRegisterEffect(PlatformEffect effect) + { + effect.Container = this; + } + + protected virtual void SetAutomationId(string id) + { + AccessibilityIdentifier = id; + } + + protected virtual void SetBackgroundColor(Color color) + { + if (color == Color.Default) + BackgroundColor = _defaultColor; + else + BackgroundColor = color.ToUIColor(); + } + + protected virtual void UpdateNativeWidget() + { + } + + internal virtual void SendVisualElementInitialized(VisualElement element, UIView nativeView) + { + element.SendViewInitialized(nativeView); + } + + void UpdateClipToBounds() + { + var clippableLayout = Element as Layout; + if (clippableLayout != null) + ClipsToBounds = clippableLayout.IsClippedToBounds; + } + } +}
\ No newline at end of file diff --git a/Xamarin.Forms.Platform.iOS/VisualElementTracker.cs b/Xamarin.Forms.Platform.iOS/VisualElementTracker.cs new file mode 100644 index 00000000..0dbd7f9a --- /dev/null +++ b/Xamarin.Forms.Platform.iOS/VisualElementTracker.cs @@ -0,0 +1,259 @@ +using System; +using System.Drawing; +using System.ComponentModel; +using System.Threading; +#if __UNIFIED__ +using UIKit; +using CoreAnimation; + +#else +using MonoTouch.UIKit; +using MonoTouch.CoreAnimation; +#endif + +namespace Xamarin.Forms.Platform.iOS +{ + public class VisualElementTracker : IDisposable + { + readonly EventHandler<EventArg<VisualElement>> _batchCommittedHandler; + + readonly PropertyChangedEventHandler _propertyChangedHandler; + readonly EventHandler _sizeChangedEventHandler; + bool _disposed; + VisualElement _element; + + // Track these by hand because the calls down into iOS are too expensive + bool _isInteractive; + Rectangle _lastBounds; + + CALayer _layer; + int _updateCount; + + public VisualElementTracker(IVisualElementRenderer renderer) + { + if (renderer == null) + throw new ArgumentNullException("renderer"); + + _propertyChangedHandler = HandlePropertyChanged; + _sizeChangedEventHandler = HandleSizeChanged; + _batchCommittedHandler = HandleRedrawNeeded; + + Renderer = renderer; + renderer.ElementChanged += OnRendererElementChanged; + SetElement(null, renderer.Element); + } + + IVisualElementRenderer Renderer { get; set; } + + public void Dispose() + { + Dispose(true); + } + + public event EventHandler NativeControlUpdated; + + protected virtual void Dispose(bool disposing) + { + if (_disposed) + return; + + _disposed = true; + + if (disposing) + { + SetElement(_element, null); + + if (_layer != null) + { + _layer.Dispose(); + _layer = null; + } + + Renderer.ElementChanged -= OnRendererElementChanged; + Renderer = null; + } + } + + void HandlePropertyChanged(object sender, PropertyChangedEventArgs e) + { + if (e.PropertyName == VisualElement.XProperty.PropertyName || e.PropertyName == VisualElement.YProperty.PropertyName || e.PropertyName == VisualElement.WidthProperty.PropertyName || + e.PropertyName == VisualElement.HeightProperty.PropertyName || e.PropertyName == VisualElement.AnchorXProperty.PropertyName || e.PropertyName == VisualElement.AnchorYProperty.PropertyName || + e.PropertyName == VisualElement.TranslationXProperty.PropertyName || e.PropertyName == VisualElement.TranslationYProperty.PropertyName || e.PropertyName == VisualElement.ScaleProperty.PropertyName || + e.PropertyName == VisualElement.RotationProperty.PropertyName || e.PropertyName == VisualElement.RotationXProperty.PropertyName || e.PropertyName == VisualElement.RotationYProperty.PropertyName || + e.PropertyName == VisualElement.IsVisibleProperty.PropertyName || e.PropertyName == VisualElement.IsEnabledProperty.PropertyName || + e.PropertyName == VisualElement.InputTransparentProperty.PropertyName || e.PropertyName == VisualElement.OpacityProperty.PropertyName) + UpdateNativeControl(); // poorly optimized + } + + void HandleRedrawNeeded(object sender, EventArgs e) + { + UpdateNativeControl(); + } + + void HandleSizeChanged(object sender, EventArgs e) + { + UpdateNativeControl(); + } + + void OnRendererElementChanged(object s, VisualElementChangedEventArgs e) + { + if (_element == e.NewElement) + return; + + SetElement(_element, e.NewElement); + } + + void OnUpdateNativeControl(CALayer caLayer) + { + var view = Renderer.Element; + var uiview = Renderer.NativeView; + + if (view == null || view.Batched) + return; + + var shouldInteract = !view.InputTransparent && view.IsEnabled; + if (_isInteractive != shouldInteract) + { + uiview.UserInteractionEnabled = shouldInteract; + _isInteractive = shouldInteract; + } + + var boundsChanged = _lastBounds != view.Bounds; + + var thread = !boundsChanged && !caLayer.Frame.IsEmpty; + + var anchorX = (float)view.AnchorX; + var anchorY = (float)view.AnchorY; + var translationX = (float)view.TranslationX; + var translationY = (float)view.TranslationY; + var rotationX = (float)view.RotationX; + var rotationY = (float)view.RotationY; + var rotation = (float)view.Rotation; + var scale = (float)view.Scale; + var width = (float)view.Width; + var height = (float)view.Height; + var x = (float)view.X; + var y = (float)view.Y; + var opacity = (float)view.Opacity; + var isVisible = view.IsVisible; + + var updateTarget = Interlocked.Increment(ref _updateCount); + + Action update = () => + { + if (updateTarget != _updateCount) + return; + + var visualElement = view; + var parent = view.RealParent; + + var shouldRelayoutSublayers = false; + if (isVisible && caLayer.Hidden) + { + caLayer.Hidden = false; + if (!caLayer.Frame.IsEmpty) + shouldRelayoutSublayers = true; + } + + if (!isVisible && !caLayer.Hidden) + { + caLayer.Hidden = true; + shouldRelayoutSublayers = true; + } + + // ripe for optimization + var transform = CATransform3D.Identity; + + // Dont ever attempt to actually change the layout of a Page unless it is a ContentPage + // iOS is a really big fan of you not actually modifying the View's of the UIViewControllers + if ((!(visualElement is Page) || visualElement is ContentPage) && width > 0 && height > 0 && parent != null && boundsChanged) + { + var target = new RectangleF(x, y, width, height); + // must reset transform prior to setting frame... + caLayer.Transform = transform; + caLayer.Frame = target; + if (shouldRelayoutSublayers) + caLayer.LayoutSublayers(); + } + else if (width <= 0 || height <= 0) + { + caLayer.Hidden = true; + return; + } + + caLayer.AnchorPoint = new PointF(anchorX, anchorY); + caLayer.Opacity = opacity; + const double epsilon = 0.001; + + // position is relative to anchor point + if (Math.Abs(anchorX - .5) > epsilon) + transform = transform.Translate((anchorX - .5f) * width, 0, 0); + if (Math.Abs(anchorY - .5) > epsilon) + transform = transform.Translate(0, (anchorY - .5f) * height, 0); + + if (Math.Abs(translationX) > epsilon || Math.Abs(translationY) > epsilon) + transform = transform.Translate(translationX, translationY, 0); + + if (Math.Abs(scale - 1) > epsilon) + transform = transform.Scale(scale); + + // not just an optimization, iOS will not "pixel align" a view which has m34 set + if (Math.Abs(rotationY % 180) > epsilon || Math.Abs(rotationX % 180) > epsilon) + transform.m34 = 1.0f / -400f; + + if (Math.Abs(rotationX % 360) > epsilon) + transform = transform.Rotate(rotationX * (float)Math.PI / 180.0f, 1.0f, 0.0f, 0.0f); + if (Math.Abs(rotationY % 360) > epsilon) + transform = transform.Rotate(rotationY * (float)Math.PI / 180.0f, 0.0f, 1.0f, 0.0f); + + transform = transform.Rotate(rotation * (float)Math.PI / 180.0f, 0.0f, 0.0f, 1.0f); + caLayer.Transform = transform; + }; + + if (thread) + CADisplayLinkTicker.Default.Invoke(update); + else + update(); + + _lastBounds = view.Bounds; + } + + void SetElement(VisualElement oldElement, VisualElement newElement) + { + if (oldElement != null) + { + oldElement.PropertyChanged -= _propertyChangedHandler; + oldElement.SizeChanged -= _sizeChangedEventHandler; + oldElement.BatchCommitted -= _batchCommittedHandler; + } + + _element = newElement; + + if (newElement != null) + { + newElement.BatchCommitted += _batchCommittedHandler; + newElement.SizeChanged += _sizeChangedEventHandler; + newElement.PropertyChanged += _propertyChangedHandler; + + UpdateNativeControl(); + } + } + + void UpdateNativeControl() + { + if (_disposed) + return; + + if (_layer == null) + { + _layer = Renderer.NativeView.Layer; + _isInteractive = Renderer.NativeView.UserInteractionEnabled; + } + + OnUpdateNativeControl(_layer); + + if (NativeControlUpdated != null) + NativeControlUpdated(this, EventArgs.Empty); + } + } +}
\ No newline at end of file diff --git a/Xamarin.Forms.Platform.iOS/Xamarin.Forms.Platform.iOS.Classic.csproj b/Xamarin.Forms.Platform.iOS/Xamarin.Forms.Platform.iOS.Classic.csproj new file mode 100644 index 00000000..7291c8f2 --- /dev/null +++ b/Xamarin.Forms.Platform.iOS/Xamarin.Forms.Platform.iOS.Classic.csproj @@ -0,0 +1,257 @@ +<?xml version="1.0" encoding="utf-8"?> +<Project ToolsVersion="4.0" DefaultTargets="Build" xmlns="http://schemas.microsoft.com/developer/msbuild/2003"> + <PropertyGroup> + <Configuration Condition=" '$(Configuration)' == '' ">Debug</Configuration> + <Platform Condition=" '$(Platform)' == '' ">iPhoneSimulator</Platform> + <ProductVersion>8.0.30703</ProductVersion> + <SchemaVersion>2.0</SchemaVersion> + <ProjectGuid>{4A47B5DF-DDFC-476B-AC41-5105FF3B9B8B}</ProjectGuid> + <ProjectTypeGuids>{6BC8ED88-2882-458C-8E55-DFD12B67127B};{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}</ProjectTypeGuids> + <OutputType>Library</OutputType> + <RootNamespace>Xamarin.Forms.Platform.iOS</RootNamespace> + <IPhoneResourcePrefix>Resources</IPhoneResourcePrefix> + <AssemblyName>Xamarin.Forms.Platform.iOS.Classic</AssemblyName> + </PropertyGroup> + <PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Debug|AnyCPU' "> + <DebugSymbols>true</DebugSymbols> + <DebugType>full</DebugType> + <Optimize>false</Optimize> + <OutputPath>classic_bin\iPhoneSimulator\Debug\</OutputPath> + <BaseIntermediateOutputPath>classic_obj\</BaseIntermediateOutputPath> + <DefineConstants>TRACE;DEBUG;__MOBILE__;__IOS__</DefineConstants> + <ErrorReport>prompt</ErrorReport> + <WarningLevel>4</WarningLevel> + <ConsolePause>false</ConsolePause> + <MtouchDebug>true</MtouchDebug> + <CodesignKey>iPhone Developer</CodesignKey> + </PropertyGroup> + <PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Release|AnyCPU' "> + <DebugType>none</DebugType> + <Optimize>true</Optimize> + <OutputPath>classic_bin\iPhoneSimulator\Release\</OutputPath> + <BaseIntermediateOutputPath>classic_obj\</BaseIntermediateOutputPath> + <ErrorReport>prompt</ErrorReport> + <WarningLevel>4</WarningLevel> + <ConsolePause>false</ConsolePause> + <CodesignKey>iPhone Developer</CodesignKey> + <DefineConstants>TRACE;__IOS__;__MOBILE__;</DefineConstants> + </PropertyGroup> + <PropertyGroup Condition="'$(Configuration)|$(Platform)' == 'Turkey|AnyCPU'"> + <DebugSymbols>true</DebugSymbols> + <OutputPath>bin\Turkey\</OutputPath> + <DefineConstants>__MOBILE__;__IOS__;TRACE;DEBUG;__MOBILE__;__IOS__</DefineConstants> + <DebugType>full</DebugType> + <PlatformTarget>AnyCPU</PlatformTarget> + <UseVSHostingProcess>false</UseVSHostingProcess> + <ErrorReport>prompt</ErrorReport> + <CodeAnalysisRuleSet>MinimumRecommendedRules.ruleset</CodeAnalysisRuleSet> + </PropertyGroup> + <ItemGroup> + <Compile Include="..\Xamarin.Forms.Core\Properties\GlobalAssemblyInfo.cs"> + <Link>Properties\GlobalAssemblyInfo.cs</Link> + </Compile> + <Compile Include="CADisplayLinkTicker.cs" /> + <Compile Include="ContextActionCell.cs" /> + <Compile Include="ContextScrollViewDelegate.cs" /> + <Compile Include="Extensions\CellExtensions.cs" /> + <Compile Include="Forms.cs" /> + <Compile Include="GlobalCloseContextGestureRecognizer.cs" /> + <Compile Include="Extensions\ArrayExtensions.cs" /> + <Compile Include="NativeViewWrapper.cs" /> + <Compile Include="NativeViewWrapperRenderer.cs" /> + <Compile Include="PlatformEffect.cs" /> + <Compile Include="LayoutExtensions.cs" /> + <Compile Include="Renderers\AlignmentExtensions.cs" /> + <Compile Include="PageExtensions.cs" /> + <Compile Include="Renderers\CarouselViewRenderer.cs" /> + <Compile Include="Renderers\ExportCellAttribute.cs" /> + <Compile Include="Renderers\ExportImageSourceHandlerAttribute.cs" /> + <Compile Include="Renderers\ExportRendererAttribute.cs" /> + <Compile Include="Resources\StringResources.Designer.cs" /> + <Compile Include="ViewInitializedEventArgs.cs" /> + </ItemGroup> + <ItemGroup> + <Compile Include="$(MSBuildThisFileDirectory)Deserializer.cs" /> + <Compile Include="$(MSBuildThisFileDirectory)ElementChangedEventArgs.cs" /> + <Compile Include="$(MSBuildThisFileDirectory)EventTracker.cs" /> + <Compile Include="$(MSBuildThisFileDirectory)FormsApplicationDelegate.cs" /> + <Compile Include="$(MSBuildThisFileDirectory)IVisualElementRenderer.cs" /> + <Compile Include="$(MSBuildThisFileDirectory)Platform.cs" /> + <Compile Include="$(MSBuildThisFileDirectory)PlatformRenderer.cs" /> + <Compile Include="$(MSBuildThisFileDirectory)RendererFactory.cs" /> + <Compile Include="$(MSBuildThisFileDirectory)RendererPool.cs" /> + <Compile Include="$(MSBuildThisFileDirectory)ResourcesProvider.cs" /> + <Compile Include="$(MSBuildThisFileDirectory)ViewRenderer.cs" /> + <Compile Include="$(MSBuildThisFileDirectory)VisualElementPackager.cs" /> + <Compile Include="$(MSBuildThisFileDirectory)VisualElementRenderer.cs" /> + <Compile Include="$(MSBuildThisFileDirectory)VisualElementTracker.cs" /> + <Compile Include="$(MSBuildThisFileDirectory)Cells\CellRenderer.cs" /> + <Compile Include="$(MSBuildThisFileDirectory)Cells\CellTableViewCell.cs" /> + <Compile Include="$(MSBuildThisFileDirectory)Cells\EntryCellRenderer.cs" /> + <Compile Include="$(MSBuildThisFileDirectory)Cells\ImageCellRenderer.cs" /> + <Compile Include="$(MSBuildThisFileDirectory)Cells\SwitchCellRenderer.cs" /> + <Compile Include="$(MSBuildThisFileDirectory)Cells\TextCellRenderer.cs" /> + <Compile Include="$(MSBuildThisFileDirectory)Cells\ViewCellRenderer.cs" /> + <Compile Include="$(MSBuildThisFileDirectory)Extensions\ColorExtensions.cs" /> + <Compile Include="$(MSBuildThisFileDirectory)Extensions\DateExtensions.cs" /> + <Compile Include="$(MSBuildThisFileDirectory)Extensions\Extensions.cs" /> + <Compile Include="$(MSBuildThisFileDirectory)Extensions\ToolbarItemExtensions.cs" /> + <Compile Include="$(MSBuildThisFileDirectory)Extensions\UIViewExtensions.cs" /> + <Compile Include="$(MSBuildThisFileDirectory)Extensions\ViewExtensions.cs" /> + <Compile Include="$(MSBuildThisFileDirectory)Renderers\ActivityIndicatorRenderer.cs" /> + <Compile Include="$(MSBuildThisFileDirectory)Renderers\BoxRenderer.cs" /> + <Compile Include="$(MSBuildThisFileDirectory)Renderers\ButtonRenderer.cs" /> + <Compile Include="$(MSBuildThisFileDirectory)Renderers\CarouselPageRenderer.cs" /> + <Compile Include="$(MSBuildThisFileDirectory)Renderers\DatePickerRenderer.cs" /> + <Compile Include="$(MSBuildThisFileDirectory)Renderers\EditorRenderer.cs" /> + <Compile Include="$(MSBuildThisFileDirectory)Renderers\EntryRenderer.cs" /> + <Compile Include="$(MSBuildThisFileDirectory)Renderers\FontExtensions.cs" /> + <Compile Include="$(MSBuildThisFileDirectory)Renderers\FormattedStringExtensions.cs" /> + <Compile Include="$(MSBuildThisFileDirectory)Renderers\FrameRenderer.cs" /> + <Compile Include="$(MSBuildThisFileDirectory)Renderers\ImageRenderer.cs" /> + <Compile Include="$(MSBuildThisFileDirectory)Renderers\KeyboardInsetTracker.cs" /> + <Compile Include="$(MSBuildThisFileDirectory)Renderers\KeyboardObserver.cs" /> + <Compile Include="$(MSBuildThisFileDirectory)Renderers\LabelRenderer.cs" /> + <Compile Include="$(MSBuildThisFileDirectory)Renderers\ListViewRenderer.cs" /> + <Compile Include="$(MSBuildThisFileDirectory)Renderers\NavigationMenuRenderer.cs" /> + <Compile Include="$(MSBuildThisFileDirectory)Renderers\NavigationRenderer.cs" /> + <Compile Include="$(MSBuildThisFileDirectory)Renderers\OpenGLViewRenderer.cs" /> + <Compile Include="$(MSBuildThisFileDirectory)Renderers\PageRenderer.cs" /> + <Compile Include="$(MSBuildThisFileDirectory)Renderers\PhoneMasterDetailRenderer.cs" /> + <Compile Include="$(MSBuildThisFileDirectory)Renderers\PickerRenderer.cs" /> + <Compile Include="$(MSBuildThisFileDirectory)Renderers\ProgressBarRenderer.cs" /> + <Compile Include="$(MSBuildThisFileDirectory)Renderers\ScrollViewRenderer.cs" /> + <Compile Include="$(MSBuildThisFileDirectory)Renderers\SearchBarRenderer.cs" /> + <Compile Include="$(MSBuildThisFileDirectory)Renderers\SliderRenderer.cs" /> + <Compile Include="$(MSBuildThisFileDirectory)Renderers\StepperRenderer.cs" /> + <Compile Include="$(MSBuildThisFileDirectory)Renderers\SwitchRenderer.cs" /> + <Compile Include="$(MSBuildThisFileDirectory)Renderers\TabbedRenderer.cs" /> + <Compile Include="$(MSBuildThisFileDirectory)Renderers\TableViewModelRenderer.cs" /> + <Compile Include="$(MSBuildThisFileDirectory)Renderers\TableViewRenderer.cs" /> + <Compile Include="$(MSBuildThisFileDirectory)Renderers\TabletMasterDetailRenderer.cs" /> + <Compile Include="$(MSBuildThisFileDirectory)Renderers\TimePickerRenderer.cs" /> + <Compile Include="$(MSBuildThisFileDirectory)Renderers\ToolbarRenderer.cs" /> + <Compile Include="$(MSBuildThisFileDirectory)Renderers\WebViewRenderer.cs" /> + <Compile Include="$(MSBuildThisFileDirectory)Properties\AssemblyInfo.cs" /> + </ItemGroup> + <ItemGroup> + <EmbeddedResource Include="Resources\StringResources.ar.resx" /> + </ItemGroup> + <ItemGroup> + <Reference Include="System" /> + <Reference Include="System.Runtime.Serialization" /> + <Reference Include="System.Xml" /> + <Reference Include="System.Core" /> + <Reference Include="monotouch" /> + <Reference Include="System.Net.Http" /> + </ItemGroup> + <ItemGroup> + <ProjectReference Include="..\Xamarin.Forms.Core\Xamarin.Forms.Core.csproj"> + <Project>{57B8B73D-C3B5-4C42-869E-7B2F17D354AC}</Project> + <Name>Xamarin.Forms.Core</Name> + </ProjectReference> + </ItemGroup> + <Import Project="$(MSBuildExtensionsPath)\Xamarin\iOS\Xamarin.MonoTouch.CSharp.targets" /> + <ItemGroup> + <EmbeddedResource Include="Resources\StringResources.ca.resx" /> + </ItemGroup> + <ItemGroup> + <EmbeddedResource Include="Resources\StringResources.cs.resx" /> + </ItemGroup> + <ItemGroup> + <EmbeddedResource Include="Resources\StringResources.da.resx" /> + </ItemGroup> + <ItemGroup> + <EmbeddedResource Include="Resources\StringResources.de.resx" /> + </ItemGroup> + <ItemGroup> + <EmbeddedResource Include="Resources\StringResources.el.resx" /> + </ItemGroup> + <ItemGroup> + <EmbeddedResource Include="Resources\StringResources.es.resx" /> + </ItemGroup> + <ItemGroup> + <EmbeddedResource Include="Resources\StringResources.fi.resx" /> + </ItemGroup> + <ItemGroup> + <EmbeddedResource Include="Resources\StringResources.fr.resx" /> + </ItemGroup> + <ItemGroup> + <EmbeddedResource Include="Resources\StringResources.he.resx" /> + </ItemGroup> + <ItemGroup> + <EmbeddedResource Include="Resources\StringResources.hi.resx" /> + </ItemGroup> + <ItemGroup> + <EmbeddedResource Include="Resources\StringResources.hr.resx" /> + </ItemGroup> + <ItemGroup> + <EmbeddedResource Include="Resources\StringResources.hu.resx" /> + </ItemGroup> + <ItemGroup> + <EmbeddedResource Include="Resources\StringResources.id.resx" /> + </ItemGroup> + <ItemGroup> + <EmbeddedResource Include="Resources\StringResources.it.resx" /> + </ItemGroup> + <ItemGroup> + <EmbeddedResource Include="Resources\StringResources.ja.resx" /> + </ItemGroup> + <ItemGroup> + <EmbeddedResource Include="Resources\StringResources.ko.resx" /> + </ItemGroup> + <ItemGroup> + <EmbeddedResource Include="Resources\StringResources.ms.resx" /> + </ItemGroup> + <ItemGroup> + <EmbeddedResource Include="Resources\StringResources.nb.resx" /> + </ItemGroup> + <ItemGroup> + <EmbeddedResource Include="Resources\StringResources.nl.resx" /> + </ItemGroup> + <ItemGroup> + <EmbeddedResource Include="Resources\StringResources.pl.resx" /> + </ItemGroup> + <ItemGroup> + <EmbeddedResource Include="Resources\StringResources.pt-BR.resx" /> + </ItemGroup> + <ItemGroup> + <EmbeddedResource Include="Resources\StringResources.pt.resx" /> + </ItemGroup> + <ItemGroup> + <EmbeddedResource Include="Resources\StringResources.resx" /> + </ItemGroup> + <ItemGroup> + <EmbeddedResource Include="Resources\StringResources.ro.resx" /> + </ItemGroup> + <ItemGroup> + <EmbeddedResource Include="Resources\StringResources.ru.resx" /> + </ItemGroup> + <ItemGroup> + <EmbeddedResource Include="Resources\StringResources.sk.resx" /> + </ItemGroup> + <ItemGroup> + <EmbeddedResource Include="Resources\StringResources.sv.resx" /> + </ItemGroup> + <ItemGroup> + <EmbeddedResource Include="Resources\StringResources.th.resx" /> + </ItemGroup> + <ItemGroup> + <EmbeddedResource Include="Resources\StringResources.tr.resx" /> + </ItemGroup> + <ItemGroup> + <EmbeddedResource Include="Resources\StringResources.uk.resx" /> + </ItemGroup> + <ItemGroup> + <EmbeddedResource Include="Resources\StringResources.vi.resx" /> + </ItemGroup> + <ItemGroup> + <EmbeddedResource Include="Resources\StringResources.zh-Hans.resx" /> + </ItemGroup> + <ItemGroup> + <EmbeddedResource Include="Resources\StringResources.zh-Hant.resx" /> + </ItemGroup> + <ItemGroup> + <EmbeddedResource Include="Resources\StringResources.zh-HK.resx" /> + </ItemGroup> +</Project>
\ No newline at end of file diff --git a/Xamarin.Forms.Platform.iOS/Xamarin.Forms.Platform.iOS.csproj b/Xamarin.Forms.Platform.iOS/Xamarin.Forms.Platform.iOS.csproj new file mode 100644 index 00000000..5afb3b33 --- /dev/null +++ b/Xamarin.Forms.Platform.iOS/Xamarin.Forms.Platform.iOS.csproj @@ -0,0 +1,191 @@ +<?xml version="1.0" encoding="utf-8"?> +<Project DefaultTargets="Build" ToolsVersion="4.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003"> + <PropertyGroup> + <Configuration Condition=" '$(Configuration)' == '' ">Debug</Configuration> + <Platform Condition=" '$(Platform)' == '' ">AnyCPU</Platform> + <ProjectTypeGuids>{FEACFBD2-3405-455C-9665-78FE426C6842};{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}</ProjectTypeGuids> + <ProjectGuid>{271193C1-6E7C-429C-A36D-3F1BE5267231}</ProjectGuid> + <OutputType>Library</OutputType> + <RootNamespace>Xamarin.Forms.Platform.iOS</RootNamespace> + <IPhoneResourcePrefix>Resources</IPhoneResourcePrefix> + <AssemblyName>Xamarin.Forms.Platform.iOS</AssemblyName> + </PropertyGroup> + <PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Debug|AnyCPU' "> + <DebugSymbols>true</DebugSymbols> + <DebugType>full</DebugType> + <Optimize>false</Optimize> + <OutputPath>bin\Debug</OutputPath> + <DefineConstants>DEBUG;</DefineConstants> + <ErrorReport>prompt</ErrorReport> + <WarningLevel>4</WarningLevel> + <ConsolePause>false</ConsolePause> + </PropertyGroup> + <PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Release|AnyCPU' "> + <DebugType>full</DebugType> + <Optimize>true</Optimize> + <OutputPath>bin\Release</OutputPath> + <ErrorReport>prompt</ErrorReport> + <WarningLevel>4</WarningLevel> + <ConsolePause>false</ConsolePause> + </PropertyGroup> + <PropertyGroup Condition="'$(Configuration)|$(Platform)' == 'Turkey|AnyCPU'"> + <DebugSymbols>true</DebugSymbols> + <OutputPath>bin\Turkey\</OutputPath> + <DefineConstants>__UNIFIED__;__MOBILE__;__IOS__;DEBUG;</DefineConstants> + <DebugType>full</DebugType> + <PlatformTarget>AnyCPU</PlatformTarget> + <UseVSHostingProcess>false</UseVSHostingProcess> + <ErrorReport>prompt</ErrorReport> + <CodeAnalysisRuleSet>MinimumRecommendedRules.ruleset</CodeAnalysisRuleSet> + </PropertyGroup> + <ItemGroup> + <Reference Include="System" /> + <Reference Include="System.Runtime.Serialization" /> + <Reference Include="System.Xml" /> + <Reference Include="System.Core" /> + <Reference Include="Xamarin.iOS" /> + <Reference Include="System.Net.Http" /> + </ItemGroup> + <ItemGroup /> + <ItemGroup> + <Compile Include="$(MSBuildThisFileDirectory)ContextActionCell.cs" /> + <Compile Include="$(MSBuildThisFileDirectory)ContextScrollViewDelegate.cs" /> + <Compile Include="$(MSBuildThisFileDirectory)GlobalCloseContextGestureRecognizer.cs" /> + <Compile Include="$(MSBuildThisFileDirectory)Deserializer.cs" /> + <Compile Include="$(MSBuildThisFileDirectory)ElementChangedEventArgs.cs" /> + <Compile Include="$(MSBuildThisFileDirectory)EventTracker.cs" /> + <Compile Include="$(MSBuildThisFileDirectory)FormsApplicationDelegate.cs" /> + <Compile Include="$(MSBuildThisFileDirectory)IVisualElementRenderer.cs" /> + <Compile Include="$(MSBuildThisFileDirectory)Platform.cs" /> + <Compile Include="$(MSBuildThisFileDirectory)PlatformRenderer.cs" /> + <Compile Include="$(MSBuildThisFileDirectory)RendererFactory.cs" /> + <Compile Include="$(MSBuildThisFileDirectory)RendererPool.cs" /> + <Compile Include="$(MSBuildThisFileDirectory)ResourcesProvider.cs" /> + <Compile Include="$(MSBuildThisFileDirectory)ViewRenderer.cs" /> + <Compile Include="$(MSBuildThisFileDirectory)VisualElementPackager.cs" /> + <Compile Include="$(MSBuildThisFileDirectory)VisualElementRenderer.cs" /> + <Compile Include="$(MSBuildThisFileDirectory)VisualElementTracker.cs" /> + <Compile Include="$(MSBuildThisFileDirectory)Cells\CellRenderer.cs" /> + <Compile Include="$(MSBuildThisFileDirectory)Cells\CellTableViewCell.cs" /> + <Compile Include="$(MSBuildThisFileDirectory)Cells\EntryCellRenderer.cs" /> + <Compile Include="$(MSBuildThisFileDirectory)Cells\ImageCellRenderer.cs" /> + <Compile Include="$(MSBuildThisFileDirectory)Cells\SwitchCellRenderer.cs" /> + <Compile Include="$(MSBuildThisFileDirectory)Cells\TextCellRenderer.cs" /> + <Compile Include="$(MSBuildThisFileDirectory)Cells\ViewCellRenderer.cs" /> + <Compile Include="$(MSBuildThisFileDirectory)Extensions\ColorExtensions.cs" /> + <Compile Include="$(MSBuildThisFileDirectory)Extensions\DateExtensions.cs" /> + <Compile Include="$(MSBuildThisFileDirectory)Extensions\Extensions.cs" /> + <Compile Include="$(MSBuildThisFileDirectory)Extensions\ToolbarItemExtensions.cs" /> + <Compile Include="$(MSBuildThisFileDirectory)Extensions\UIViewExtensions.cs" /> + <Compile Include="$(MSBuildThisFileDirectory)Extensions\ViewExtensions.cs" /> + <Compile Include="$(MSBuildThisFileDirectory)Renderers\ActivityIndicatorRenderer.cs" /> + <Compile Include="$(MSBuildThisFileDirectory)Renderers\BoxRenderer.cs" /> + <Compile Include="$(MSBuildThisFileDirectory)Renderers\ButtonRenderer.cs" /> + <Compile Include="$(MSBuildThisFileDirectory)Renderers\CarouselPageRenderer.cs" /> + <Compile Include="$(MSBuildThisFileDirectory)Renderers\DatePickerRenderer.cs" /> + <Compile Include="$(MSBuildThisFileDirectory)Renderers\EditorRenderer.cs" /> + <Compile Include="$(MSBuildThisFileDirectory)Renderers\EntryRenderer.cs" /> + <Compile Include="$(MSBuildThisFileDirectory)Renderers\FontExtensions.cs" /> + <Compile Include="$(MSBuildThisFileDirectory)Renderers\FormattedStringExtensions.cs" /> + <Compile Include="$(MSBuildThisFileDirectory)Renderers\FrameRenderer.cs" /> + <Compile Include="$(MSBuildThisFileDirectory)Renderers\ImageRenderer.cs" /> + <Compile Include="$(MSBuildThisFileDirectory)Renderers\KeyboardInsetTracker.cs" /> + <Compile Include="$(MSBuildThisFileDirectory)Renderers\KeyboardObserver.cs" /> + <Compile Include="$(MSBuildThisFileDirectory)Renderers\LabelRenderer.cs" /> + <Compile Include="$(MSBuildThisFileDirectory)Renderers\ListViewRenderer.cs" /> + <Compile Include="$(MSBuildThisFileDirectory)Renderers\NavigationMenuRenderer.cs" /> + <Compile Include="$(MSBuildThisFileDirectory)Renderers\NavigationRenderer.cs" /> + <Compile Include="$(MSBuildThisFileDirectory)Renderers\OpenGLViewRenderer.cs" /> + <Compile Include="$(MSBuildThisFileDirectory)Renderers\PageRenderer.cs" /> + <Compile Include="$(MSBuildThisFileDirectory)Renderers\PhoneMasterDetailRenderer.cs" /> + <Compile Include="$(MSBuildThisFileDirectory)Renderers\PickerRenderer.cs" /> + <Compile Include="$(MSBuildThisFileDirectory)Renderers\ProgressBarRenderer.cs" /> + <Compile Include="$(MSBuildThisFileDirectory)Renderers\ScrollViewRenderer.cs" /> + <Compile Include="$(MSBuildThisFileDirectory)Renderers\SearchBarRenderer.cs" /> + <Compile Include="$(MSBuildThisFileDirectory)Renderers\SliderRenderer.cs" /> + <Compile Include="$(MSBuildThisFileDirectory)Renderers\StepperRenderer.cs" /> + <Compile Include="$(MSBuildThisFileDirectory)Renderers\SwitchRenderer.cs" /> + <Compile Include="$(MSBuildThisFileDirectory)Renderers\TabbedRenderer.cs" /> + <Compile Include="$(MSBuildThisFileDirectory)Renderers\TableViewModelRenderer.cs" /> + <Compile Include="$(MSBuildThisFileDirectory)Renderers\TableViewRenderer.cs" /> + <Compile Include="$(MSBuildThisFileDirectory)Renderers\TabletMasterDetailRenderer.cs" /> + <Compile Include="$(MSBuildThisFileDirectory)Renderers\TimePickerRenderer.cs" /> + <Compile Include="$(MSBuildThisFileDirectory)Renderers\ToolbarRenderer.cs" /> + <Compile Include="$(MSBuildThisFileDirectory)Renderers\WebViewRenderer.cs" /> + <Compile Include="$(MSBuildThisFileDirectory)Properties\AssemblyInfo.cs" /> + <Compile Include="$(MSBuildThisFileDirectory)Extensions\CellExtensions.cs" /> + <Compile Include="CADisplayLinkTicker.cs" /> + <Compile Include="ExportCellAttribute.cs" /> + <Compile Include="ExportImageSourceHandlerAttribute.cs" /> + <Compile Include="ExportRendererAttribute.cs" /> + <Compile Include="Extensions\ArrayExtensions.cs" /> + <Compile Include="NativeViewWrapper.cs" /> + <Compile Include="NativeViewWrapperRenderer.cs" /> + <Compile Include="PlatformEffect.cs" /> + <Compile Include="LayoutExtensions.cs" /> + <Compile Include="Renderers\AlignmentExtensions.cs" /> + <Compile Include="Forms.cs" /> + <Compile Include="PageExtensions.cs" /> + <Compile Include="Renderers\CarouselViewRenderer.cs" /> + <Compile Include="Resources\StringResources.Designer.cs"> + <AutoGen>True</AutoGen> + <DesignTime>True</DesignTime> + <DependentUpon>StringResources.resx</DependentUpon> + </Compile> + <Compile Include="ViewInitializedEventArgs.cs" /> + </ItemGroup> + <ItemGroup> + <EmbeddedResource Include="Resources\StringResources.ar.resx" /> + <EmbeddedResource Include="Resources\StringResources.ca.resx" /> + <EmbeddedResource Include="Resources\StringResources.cs.resx" /> + <EmbeddedResource Include="Resources\StringResources.da.resx" /> + <EmbeddedResource Include="Resources\StringResources.de.resx"> + <SubType>Designer</SubType> + </EmbeddedResource> + <EmbeddedResource Include="Resources\StringResources.el.resx" /> + <EmbeddedResource Include="Resources\StringResources.es.resx" /> + <EmbeddedResource Include="Resources\StringResources.fi.resx" /> + <EmbeddedResource Include="Resources\StringResources.fr.resx" /> + <EmbeddedResource Include="Resources\StringResources.he.resx" /> + <EmbeddedResource Include="Resources\StringResources.hi.resx" /> + <EmbeddedResource Include="Resources\StringResources.hr.resx" /> + <EmbeddedResource Include="Resources\StringResources.hu.resx" /> + <EmbeddedResource Include="Resources\StringResources.id.resx" /> + <EmbeddedResource Include="Resources\StringResources.it.resx" /> + <EmbeddedResource Include="Resources\StringResources.ja.resx" /> + <EmbeddedResource Include="Resources\StringResources.ko.resx" /> + <EmbeddedResource Include="Resources\StringResources.ms.resx" /> + <EmbeddedResource Include="Resources\StringResources.nb.resx" /> + <EmbeddedResource Include="Resources\StringResources.nl.resx" /> + <EmbeddedResource Include="Resources\StringResources.pl.resx" /> + <EmbeddedResource Include="Resources\StringResources.pt-BR.resx" /> + <EmbeddedResource Include="Resources\StringResources.pt.resx" /> + <EmbeddedResource Include="Resources\StringResources.resx"> + <Generator>ResXFileCodeGenerator</Generator> + <LastGenOutput>StringResources.Designer.cs</LastGenOutput> + </EmbeddedResource> + <EmbeddedResource Include="Resources\StringResources.ro.resx" /> + <EmbeddedResource Include="Resources\StringResources.ru.resx" /> + <EmbeddedResource Include="Resources\StringResources.sk.resx" /> + <EmbeddedResource Include="Resources\StringResources.sv.resx" /> + <EmbeddedResource Include="Resources\StringResources.th.resx" /> + <EmbeddedResource Include="Resources\StringResources.tr.resx" /> + <EmbeddedResource Include="Resources\StringResources.uk.resx" /> + <EmbeddedResource Include="Resources\StringResources.vi.resx" /> + <EmbeddedResource Include="Resources\StringResources.zh-Hans.resx" /> + <EmbeddedResource Include="Resources\StringResources.zh-Hant.resx" /> + <EmbeddedResource Include="Resources\StringResources.zh-HK.resx" /> + </ItemGroup> + <Import Project="$(MSBuildExtensionsPath)\Xamarin\iOS\Xamarin.iOS.CSharp.targets" /> + <ItemGroup> + <ProjectReference Include="..\Xamarin.Forms.Core\Xamarin.Forms.Core.csproj"> + <Project>{57B8B73D-C3B5-4C42-869E-7B2F17D354AC}</Project> + <Name>Xamarin.Forms.Core</Name> + </ProjectReference> + </ItemGroup> + <ItemGroup> + <Compile Include="..\Xamarin.Forms.Core\Properties\GlobalAssemblyInfo.cs"> + <Link>Properties\GlobalAssemblyInfo.cs</Link> + </Compile> + </ItemGroup> +</Project>
\ No newline at end of file diff --git a/Xamarin.Forms.Platform.iOS/Xamarin.Forms.Platform.iOS.nuspec b/Xamarin.Forms.Platform.iOS/Xamarin.Forms.Platform.iOS.nuspec new file mode 100644 index 00000000..6ecf64c4 --- /dev/null +++ b/Xamarin.Forms.Platform.iOS/Xamarin.Forms.Platform.iOS.nuspec @@ -0,0 +1,14 @@ +<?xml version="1.0"?> +<package > + <metadata> + <id>Xamarin.Forms.Platform.iOS</id> + <version>$version$</version> + <title>Xamarin.Forms iOS Platform Renderers</title> + <authors>Xamarin Inc.</authors> + <owners>Xamarin Inc.</owners> + <requireLicenseAcceptance>false</requireLicenseAcceptance> + <description>Xamarin.Forms platform abstruction library for mobile applications.</description> + <releaseNotes></releaseNotes> + <copyright>Copyright 2013</copyright> + </metadata> +</package> |