summaryrefslogtreecommitdiff
path: root/Xamarin.Forms.Platform.MacOS/Cells
diff options
context:
space:
mode:
Diffstat (limited to 'Xamarin.Forms.Platform.MacOS/Cells')
-rw-r--r--Xamarin.Forms.Platform.MacOS/Cells/CellNSView.cs167
-rw-r--r--Xamarin.Forms.Platform.MacOS/Cells/CellRenderer.cs68
-rw-r--r--Xamarin.Forms.Platform.MacOS/Cells/EntryCellRenderer.cs144
-rw-r--r--Xamarin.Forms.Platform.MacOS/Cells/ImageCellRenderer.cs68
-rw-r--r--Xamarin.Forms.Platform.MacOS/Cells/NSTableViewCellStyle.cs12
-rw-r--r--Xamarin.Forms.Platform.MacOS/Cells/SwitchCellRenderer.cs88
-rw-r--r--Xamarin.Forms.Platform.MacOS/Cells/TextCellRenderer.cs66
-rw-r--r--Xamarin.Forms.Platform.MacOS/Cells/ViewCellNSView.cs105
-rw-r--r--Xamarin.Forms.Platform.MacOS/Cells/ViewCellRenderer.cs46
9 files changed, 764 insertions, 0 deletions
diff --git a/Xamarin.Forms.Platform.MacOS/Cells/CellNSView.cs b/Xamarin.Forms.Platform.MacOS/Cells/CellNSView.cs
new file mode 100644
index 00000000..1ba964ee
--- /dev/null
+++ b/Xamarin.Forms.Platform.MacOS/Cells/CellNSView.cs
@@ -0,0 +1,167 @@
+using System;
+using System.ComponentModel;
+using AppKit;
+using CoreGraphics;
+
+namespace Xamarin.Forms.Platform.MacOS
+{
+ internal class CellNSView : NSView, INativeElementView
+ {
+ static readonly NSColor s_defaultChildViewsBackground = NSColor.Clear;
+ static readonly CGColor s_defaultHeaderViewsBackground = NSColor.LightGray.CGColor;
+ Cell _cell;
+ readonly NSTableViewCellStyle _style;
+
+ public Action<object, PropertyChangedEventArgs> PropertyChanged;
+
+ public CellNSView(NSTableViewCellStyle style)
+ {
+ WantsLayer = true;
+ _style = style;
+ CreateUI();
+ }
+
+ public NSTextField TextLabel { get; private set; }
+
+ public NSTextField DetailTextLabel { get; private set; }
+
+ public NSImageView ImageView { get; private set; }
+
+ public NSView AccessoryView { get; private set; }
+
+ public Element Element => Cell;
+
+ public Cell Cell
+ {
+ get { return _cell; }
+ set
+ {
+ if (_cell == value)
+ return;
+
+ ICellController cellController = _cell;
+
+ if (cellController != null)
+ Device.BeginInvokeOnMainThread(cellController.SendDisappearing);
+
+ _cell = value;
+ cellController = value;
+
+ if (cellController != null)
+ Device.BeginInvokeOnMainThread(cellController.SendAppearing);
+ }
+ }
+
+ public void HandlePropertyChanged(object sender, PropertyChangedEventArgs e)
+ {
+ PropertyChanged?.Invoke(this, e);
+ }
+
+ public override void Layout()
+ {
+ const int padding = 10;
+ nfloat availableHeight = Frame.Height;
+ nfloat availableWidth = Frame.Width - padding * 2;
+ nfloat imageWidth = 0;
+ nfloat accessoryViewWidth = 0;
+
+ if (ImageView != null)
+ {
+ nfloat imageHeight = imageWidth = availableHeight;
+ ImageView.Frame = new CGRect(padding, 0, imageWidth, imageHeight);
+ }
+
+ if (AccessoryView != null)
+ {
+ accessoryViewWidth = _style == NSTableViewCellStyle.Value1 ? 50 : availableWidth - 100;
+ AccessoryView.Frame = new CGRect(availableWidth - accessoryViewWidth + padding, 0, accessoryViewWidth,
+ availableHeight);
+ foreach (var subView in AccessoryView.Subviews)
+ {
+ //try to find the size the control wants, if no width use default width
+ var size = subView.FittingSize;
+ if (size.Width == 0)
+ size.Width = accessoryViewWidth;
+
+ var x = AccessoryView.Bounds.Width - size.Width;
+ var y = (AccessoryView.Bounds.Height - size.Height) / 2;
+ subView.Frame = new CGRect(new CGPoint(x, y), size);
+ }
+ }
+
+ nfloat labelHeights = availableHeight;
+ nfloat labelWidth = availableWidth - imageWidth - accessoryViewWidth;
+
+ if (!string.IsNullOrEmpty(DetailTextLabel?.StringValue))
+ {
+ labelHeights = availableHeight / 2;
+ DetailTextLabel.CenterTextVertically(new CGRect(imageWidth + padding, 0, labelWidth, labelHeights));
+ }
+
+ TextLabel.CenterTextVertically(new CGRect(imageWidth + padding, availableHeight - labelHeights, labelWidth,
+ labelHeights));
+ base.Layout();
+ }
+
+ internal static NSView GetNativeCell(NSTableView tableView, Cell cell, string templateId = "", bool isHeader = false,
+ bool isRecycle = false)
+ {
+ var reusable = tableView.MakeView(templateId, tableView);
+ NSView nativeCell;
+ if (reusable == null || !isRecycle)
+ {
+ var renderer = (CellRenderer)Registrar.Registered.GetHandler(cell.GetType());
+ nativeCell = renderer.GetCell(cell, null, tableView);
+ }
+ else
+ {
+ nativeCell = reusable;
+ }
+
+ if (string.IsNullOrEmpty(nativeCell.Identifier))
+ nativeCell.Identifier = templateId;
+
+ if (!isHeader) return nativeCell;
+ if (nativeCell.Layer != null) nativeCell.Layer.BackgroundColor = s_defaultHeaderViewsBackground;
+ return nativeCell;
+ }
+
+ void CreateUI()
+ {
+ var style = _style;
+
+ AddSubview(TextLabel = new NSTextField
+ {
+ Bordered = false,
+ Selectable = false,
+ Editable = false,
+ Font = NSFont.LabelFontOfSize(NSFont.SystemFontSize)
+ });
+
+ TextLabel.Cell.BackgroundColor = s_defaultChildViewsBackground;
+
+ if (style == NSTableViewCellStyle.Image || style == NSTableViewCellStyle.Subtitle ||
+ style == NSTableViewCellStyle.ImageSubtitle)
+ {
+ AddSubview(DetailTextLabel = new NSTextField
+ {
+ Bordered = false,
+ Selectable = false,
+ Editable = false,
+ Font = NSFont.LabelFontOfSize(NSFont.SmallSystemFontSize)
+ });
+ DetailTextLabel.Cell.BackgroundColor = s_defaultChildViewsBackground;
+ }
+
+ if (style == NSTableViewCellStyle.Image || style == NSTableViewCellStyle.ImageSubtitle)
+ AddSubview(ImageView = new NSImageView());
+
+ if (style == NSTableViewCellStyle.Value1 || style == NSTableViewCellStyle.Value2)
+ {
+ var accessoryView = new NSView { WantsLayer = true };
+ accessoryView.Layer.BackgroundColor = s_defaultChildViewsBackground.CGColor;
+ AddSubview(AccessoryView = accessoryView);
+ }
+ }
+ }
+} \ No newline at end of file
diff --git a/Xamarin.Forms.Platform.MacOS/Cells/CellRenderer.cs b/Xamarin.Forms.Platform.MacOS/Cells/CellRenderer.cs
new file mode 100644
index 00000000..54e540a6
--- /dev/null
+++ b/Xamarin.Forms.Platform.MacOS/Cells/CellRenderer.cs
@@ -0,0 +1,68 @@
+using System;
+using AppKit;
+using CoreGraphics;
+
+namespace Xamarin.Forms.Platform.MacOS
+{
+ public class CellRenderer : IRegisterable
+ {
+ static readonly BindableProperty s_realCellProperty = BindableProperty.CreateAttached("RealCell", typeof(NSView),
+ typeof(Cell), null);
+
+ EventHandler _onForceUpdateSizeRequested;
+
+ public virtual NSView GetCell(Cell item, NSView reusableView, NSTableView tv)
+ {
+ var tvc = reusableView as CellNSView ?? new CellNSView(NSTableViewCellStyle.Default);
+
+ tvc.Cell = item;
+
+ WireUpForceUpdateSizeRequested(item, tvc, tv);
+
+ tvc.TextLabel.StringValue = item.ToString();
+
+ UpdateBackground(tvc, item);
+
+ return tvc;
+ }
+
+ protected void UpdateBackground(NSView tableViewCell, Cell cell)
+ {
+ tableViewCell.WantsLayer = true;
+ var bgColor = NSColor.White;
+ var element = cell.RealParent as VisualElement;
+ if (element != null)
+ bgColor = element.BackgroundColor == Color.Default ? bgColor : element.BackgroundColor.ToNSColor();
+
+ UpdateBackgroundChild(cell, bgColor);
+
+ tableViewCell.Layer.BackgroundColor = bgColor.CGColor;
+ }
+
+ protected void WireUpForceUpdateSizeRequested(ICellController cell, NSView nativeCell, NSTableView tableView)
+ {
+ cell.ForceUpdateSizeRequested -= _onForceUpdateSizeRequested;
+
+ _onForceUpdateSizeRequested = (sender, e) =>
+ {
+ //TODO: Implement ForceUpdateSize
+ };
+
+ cell.ForceUpdateSizeRequested += _onForceUpdateSizeRequested;
+ }
+
+ internal virtual void UpdateBackgroundChild(Cell cell, NSColor backgroundColor)
+ {
+ }
+
+ internal static NSView GetRealCell(BindableObject cell)
+ {
+ return (NSView)cell.GetValue(s_realCellProperty);
+ }
+
+ internal static void SetRealCell(BindableObject cell, NSView renderer)
+ {
+ cell.SetValue(s_realCellProperty, renderer);
+ }
+ }
+} \ No newline at end of file
diff --git a/Xamarin.Forms.Platform.MacOS/Cells/EntryCellRenderer.cs b/Xamarin.Forms.Platform.MacOS/Cells/EntryCellRenderer.cs
new file mode 100644
index 00000000..43789dcc
--- /dev/null
+++ b/Xamarin.Forms.Platform.MacOS/Cells/EntryCellRenderer.cs
@@ -0,0 +1,144 @@
+using System;
+using System.ComponentModel;
+using AppKit;
+using CoreGraphics;
+using Foundation;
+
+namespace Xamarin.Forms.Platform.MacOS
+{
+ public class EntryCellRenderer : CellRenderer
+ {
+ static readonly Color s_defaultTextColor = Color.Black;
+
+ public override NSView GetCell(Cell item, NSView reusableView, NSTableView tv)
+ {
+ NSTextField nsEntry = null;
+ var tvc = reusableView as CellNSView;
+ if (tvc == null)
+ tvc = new CellNSView(NSTableViewCellStyle.Value2);
+ else
+ {
+ tvc.Cell.PropertyChanged -= OnCellPropertyChanged;
+
+ nsEntry = tvc.AccessoryView.Subviews[0] as NSTextField;
+ if (nsEntry != null)
+ {
+ nsEntry.RemoveFromSuperview();
+ nsEntry.Changed -= OnTextFieldTextChanged;
+ }
+ }
+
+ SetRealCell(item, tvc);
+
+ if (nsEntry == null)
+ tvc.AccessoryView.AddSubview(nsEntry = new NSTextField());
+
+ var entryCell = (EntryCell)item;
+
+ tvc.Cell = item;
+ tvc.Cell.PropertyChanged += OnCellPropertyChanged;
+ nsEntry.Changed += OnTextFieldTextChanged;
+
+ WireUpForceUpdateSizeRequested(item, tvc, tv);
+
+ UpdateBackground(tvc, entryCell);
+ UpdateLabel(tvc, entryCell);
+ UpdateText(tvc, entryCell);
+ UpdatePlaceholder(tvc, entryCell);
+ UpdateLabelColor(tvc, entryCell);
+ UpdateHorizontalTextAlignment(tvc, entryCell);
+ UpdateIsEnabled(tvc, entryCell);
+
+ return tvc;
+ }
+
+ internal override void UpdateBackgroundChild(Cell cell, NSColor backgroundColor)
+ {
+ var realCell = (CellNSView)GetRealCell(cell);
+
+ var nsTextField = realCell.AccessoryView.Subviews[0] as NSTextField;
+ if (nsTextField != null)
+ nsTextField.BackgroundColor = backgroundColor;
+
+ base.UpdateBackgroundChild(cell, backgroundColor);
+ }
+
+ static void OnCellPropertyChanged(object sender, PropertyChangedEventArgs e)
+ {
+ var entryCell = (EntryCell)sender;
+ var realCell = (CellNSView)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.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 OnTextFieldTextChanged(object sender, EventArgs eventArgs)
+ {
+ var notification = (NSNotification)sender;
+ var view = (NSView)notification.Object;
+ var field = (NSTextField)view;
+
+ CellNSView realCell = null;
+ while (view.Superview != null && realCell == null)
+ {
+ view = view.Superview;
+ realCell = view as CellNSView;
+ }
+
+ if (realCell != null)
+ ((EntryCell)realCell.Cell).Text = field.StringValue;
+ }
+
+ static void UpdateHorizontalTextAlignment(CellNSView cell, EntryCell entryCell)
+ {
+ var nsTextField = cell.AccessoryView.Subviews[0] as NSTextField;
+ if (nsTextField != null)
+ nsTextField.Alignment = entryCell.HorizontalTextAlignment.ToNativeTextAlignment();
+ }
+
+ static void UpdateIsEnabled(CellNSView cell, EntryCell entryCell)
+ {
+ cell.TextLabel.Enabled = entryCell.IsEnabled;
+ var nsTextField = cell.AccessoryView.Subviews[0] as NSTextField;
+ if (nsTextField != null)
+ nsTextField.Enabled = entryCell.IsEnabled;
+ }
+
+ static void UpdateLabel(CellNSView cell, EntryCell entryCell)
+ {
+ cell.TextLabel.StringValue = entryCell.Label ?? "";
+ }
+
+ static void UpdateLabelColor(CellNSView cell, EntryCell entryCell)
+ {
+ cell.TextLabel.TextColor = entryCell.LabelColor.ToNSColor(s_defaultTextColor);
+ }
+
+ static void UpdatePlaceholder(CellNSView cell, EntryCell entryCell)
+ {
+ var nsTextField = cell.AccessoryView.Subviews[0] as NSTextField;
+ if (nsTextField != null)
+ nsTextField.PlaceholderString = entryCell.Placeholder ?? "";
+ }
+
+ static void UpdateText(CellNSView cell, EntryCell entryCell)
+ {
+ var nsTextField = cell.AccessoryView.Subviews[0] as NSTextField;
+ if (nsTextField != null && nsTextField.StringValue == entryCell.Text)
+ return;
+
+ if (nsTextField != null)
+ nsTextField.StringValue = entryCell.Text ?? "";
+ }
+ }
+} \ No newline at end of file
diff --git a/Xamarin.Forms.Platform.MacOS/Cells/ImageCellRenderer.cs b/Xamarin.Forms.Platform.MacOS/Cells/ImageCellRenderer.cs
new file mode 100644
index 00000000..8bd76772
--- /dev/null
+++ b/Xamarin.Forms.Platform.MacOS/Cells/ImageCellRenderer.cs
@@ -0,0 +1,68 @@
+using System.ComponentModel;
+using System.Threading.Tasks;
+using AppKit;
+using Foundation;
+
+namespace Xamarin.Forms.Platform.MacOS
+{
+ public class ImageCellRenderer : TextCellRenderer
+ {
+ public override NSView GetCell(Cell item, NSView reusableView, NSTableView tv)
+ {
+ var tvc = reusableView as CellNSView ?? new CellNSView(NSTableViewCellStyle.ImageSubtitle);
+
+ var result = (CellNSView)base.GetCell(item, tvc, tv);
+
+ var imageCell = (ImageCell)item;
+
+ WireUpForceUpdateSizeRequested(item, result, tv);
+
+#pragma warning disable CS4014 // Because this call is not awaited, execution of the current method continues before the call is completed
+ SetImage(imageCell, result);
+#pragma warning restore CS4014 // Because this call is not awaited, execution of the current method continues before the call is completed
+
+ return result;
+ }
+
+ protected override async void HandlePropertyChanged(object sender, PropertyChangedEventArgs args)
+ {
+ var tvc = (CellNSView)sender;
+ var imageCell = (ImageCell)tvc.Cell;
+
+ base.HandlePropertyChanged(sender, args);
+
+ if (args.PropertyName == ImageCell.ImageSourceProperty.PropertyName)
+ await SetImage(imageCell, tvc);
+ }
+
+ static async Task SetImage(ImageCell cell, CellNSView target)
+ {
+ var source = cell.ImageSource;
+
+ target.ImageView.Image = null;
+
+ IImageSourceHandler handler;
+
+ if (source != null && (handler = Registrar.Registered.GetHandler<IImageSourceHandler>(source.GetType())) != null)
+ {
+ NSImage uiimage;
+ try
+ {
+ uiimage = await handler.LoadImageAsync(source).ConfigureAwait(false);
+ }
+ catch (TaskCanceledException)
+ {
+ uiimage = null;
+ }
+
+ NSRunLoop.Main.BeginInvokeOnMainThread(() =>
+ {
+ target.ImageView.Image = uiimage;
+ target.NeedsLayout = true;
+ });
+ }
+ else
+ target.ImageView.Image = null;
+ }
+ }
+} \ No newline at end of file
diff --git a/Xamarin.Forms.Platform.MacOS/Cells/NSTableViewCellStyle.cs b/Xamarin.Forms.Platform.MacOS/Cells/NSTableViewCellStyle.cs
new file mode 100644
index 00000000..3e0235d4
--- /dev/null
+++ b/Xamarin.Forms.Platform.MacOS/Cells/NSTableViewCellStyle.cs
@@ -0,0 +1,12 @@
+namespace Xamarin.Forms.Platform.MacOS
+{
+ internal enum NSTableViewCellStyle
+ {
+ Default,
+ Value1,
+ Value2,
+ Subtitle,
+ Image,
+ ImageSubtitle
+ }
+} \ No newline at end of file
diff --git a/Xamarin.Forms.Platform.MacOS/Cells/SwitchCellRenderer.cs b/Xamarin.Forms.Platform.MacOS/Cells/SwitchCellRenderer.cs
new file mode 100644
index 00000000..5f086b49
--- /dev/null
+++ b/Xamarin.Forms.Platform.MacOS/Cells/SwitchCellRenderer.cs
@@ -0,0 +1,88 @@
+using System;
+using System.ComponentModel;
+using AppKit;
+
+namespace Xamarin.Forms.Platform.MacOS
+{
+ public class SwitchCellRenderer : CellRenderer
+ {
+ public override NSView GetCell(Cell item, NSView reusableView, NSTableView tv)
+ {
+ var tvc = reusableView as CellNSView;
+ NSButton nsSwitch = null;
+ if (tvc == null)
+ tvc = new CellNSView(NSTableViewCellStyle.Value1);
+ else
+ {
+ nsSwitch = tvc.AccessoryView.Subviews[0] as NSButton;
+ if (nsSwitch != null)
+ {
+ nsSwitch.RemoveFromSuperview();
+ nsSwitch.Activated -= OnSwitchValueChanged;
+ }
+ tvc.Cell.PropertyChanged -= OnCellPropertyChanged;
+ }
+
+ SetRealCell(item, tvc);
+
+ if (nsSwitch == null)
+ {
+ nsSwitch = new NSButton { AllowsMixedState = false, Title = string.Empty };
+ nsSwitch.SetButtonType(NSButtonType.Switch);
+ }
+
+ var boolCell = (SwitchCell)item;
+
+ tvc.Cell = item;
+ tvc.Cell.PropertyChanged += OnCellPropertyChanged;
+ tvc.AccessoryView.AddSubview(nsSwitch);
+ tvc.TextLabel.StringValue = boolCell.Text ?? "";
+
+ nsSwitch.State = boolCell.On ? NSCellStateValue.On : NSCellStateValue.Off;
+ nsSwitch.Activated += OnSwitchValueChanged;
+ WireUpForceUpdateSizeRequested(item, tvc, tv);
+
+ UpdateBackground(tvc, item);
+ UpdateIsEnabled(tvc, boolCell);
+
+ return tvc;
+ }
+
+ static void UpdateIsEnabled(CellNSView cell, SwitchCell switchCell)
+ {
+ cell.TextLabel.Enabled = switchCell.IsEnabled;
+ var uiSwitch = cell.AccessoryView.Subviews[0] as NSButton;
+ if (uiSwitch != null)
+ uiSwitch.Enabled = switchCell.IsEnabled;
+ }
+
+ void OnCellPropertyChanged(object sender, PropertyChangedEventArgs e)
+ {
+ var boolCell = (SwitchCell)sender;
+ var realCell = (CellNSView)GetRealCell(boolCell);
+
+ if (e.PropertyName == SwitchCell.OnProperty.PropertyName)
+ ((NSButton)realCell.AccessoryView.Subviews[0]).State = boolCell.On ? NSCellStateValue.On : NSCellStateValue.Off;
+ else if (e.PropertyName == SwitchCell.TextProperty.PropertyName)
+ realCell.TextLabel.StringValue = boolCell.Text ?? "";
+ else if (e.PropertyName == Cell.IsEnabledProperty.PropertyName)
+ UpdateIsEnabled(realCell, boolCell);
+ }
+
+ void OnSwitchValueChanged(object sender, EventArgs eventArgs)
+ {
+ var view = (NSView)sender;
+ var sw = (NSButton)view;
+
+ CellNSView realCell = null;
+ while (view.Superview != null && realCell == null)
+ {
+ view = view.Superview;
+ realCell = view as CellNSView;
+ }
+
+ if (realCell != null)
+ ((SwitchCell)realCell.Cell).On = sw.State == NSCellStateValue.On;
+ }
+ }
+} \ No newline at end of file
diff --git a/Xamarin.Forms.Platform.MacOS/Cells/TextCellRenderer.cs b/Xamarin.Forms.Platform.MacOS/Cells/TextCellRenderer.cs
new file mode 100644
index 00000000..6e36ce7d
--- /dev/null
+++ b/Xamarin.Forms.Platform.MacOS/Cells/TextCellRenderer.cs
@@ -0,0 +1,66 @@
+using System.ComponentModel;
+using AppKit;
+
+namespace Xamarin.Forms.Platform.MacOS
+{
+ public class TextCellRenderer : CellRenderer
+ {
+ static readonly Color s_defaultDetailColor = new Color(.32, .4, .57);
+ static readonly Color s_defaultTextColor = Color.Black;
+
+ public override NSView GetCell(Cell item, NSView reusableView, NSTableView tv)
+ {
+ var textCell = (TextCell)item;
+
+ var tvc = reusableView as CellNSView ?? new CellNSView(NSTableViewCellStyle.Subtitle);
+
+ if (tvc.Cell != null)
+ tvc.Cell.PropertyChanged -= tvc.HandlePropertyChanged;
+
+ tvc.Cell = textCell;
+ textCell.PropertyChanged += tvc.HandlePropertyChanged;
+ tvc.PropertyChanged = HandlePropertyChanged;
+
+ tvc.TextLabel.StringValue = textCell.Text ?? "";
+ tvc.DetailTextLabel.StringValue = textCell.Detail ?? "";
+ tvc.TextLabel.TextColor = textCell.TextColor.ToNSColor(s_defaultTextColor);
+ tvc.DetailTextLabel.TextColor = textCell.DetailColor.ToNSColor(s_defaultDetailColor);
+
+ WireUpForceUpdateSizeRequested(item, tvc, tv);
+
+ UpdateIsEnabled(tvc, textCell);
+
+ UpdateBackground(tvc, item);
+
+ return tvc;
+ }
+
+ protected virtual void HandlePropertyChanged(object sender, PropertyChangedEventArgs args)
+ {
+ var tvc = (CellNSView)sender;
+ var textCell = (TextCell)tvc.Cell;
+ if (args.PropertyName == TextCell.TextProperty.PropertyName)
+ {
+ tvc.TextLabel.StringValue = textCell.Text ?? "";
+ tvc.TextLabel.SizeToFit();
+ }
+ else if (args.PropertyName == TextCell.DetailProperty.PropertyName)
+ {
+ tvc.DetailTextLabel.StringValue = textCell.Detail ?? "";
+ tvc.DetailTextLabel.SizeToFit();
+ }
+ else if (args.PropertyName == TextCell.TextColorProperty.PropertyName)
+ tvc.TextLabel.TextColor = textCell.TextColor.ToNSColor(s_defaultTextColor);
+ else if (args.PropertyName == TextCell.DetailColorProperty.PropertyName)
+ tvc.DetailTextLabel.TextColor = textCell.DetailColor.ToNSColor(s_defaultTextColor);
+ else if (args.PropertyName == Cell.IsEnabledProperty.PropertyName)
+ UpdateIsEnabled(tvc, textCell);
+ }
+
+ static void UpdateIsEnabled(CellNSView cell, TextCell entryCell)
+ {
+ cell.TextLabel.Enabled = entryCell.IsEnabled;
+ cell.DetailTextLabel.Enabled = entryCell.IsEnabled;
+ }
+ }
+} \ No newline at end of file
diff --git a/Xamarin.Forms.Platform.MacOS/Cells/ViewCellNSView.cs b/Xamarin.Forms.Platform.MacOS/Cells/ViewCellNSView.cs
new file mode 100644
index 00000000..0dd766a2
--- /dev/null
+++ b/Xamarin.Forms.Platform.MacOS/Cells/ViewCellNSView.cs
@@ -0,0 +1,105 @@
+using System;
+using AppKit;
+using RectangleF = CoreGraphics.CGRect;
+
+namespace Xamarin.Forms.Platform.MacOS
+{
+ public class ViewCellNSView : NSView, INativeElementView
+ {
+ WeakReference<IVisualElementRenderer> _rendererRef;
+
+ ViewCell _viewCell;
+
+ public Element Element => ViewCell;
+
+ public ViewCell ViewCell
+ {
+ get { return _viewCell; }
+ set
+ {
+ if (_viewCell == value)
+ return;
+ UpdateCell(value);
+ }
+ }
+
+ public override void Layout()
+ {
+ LayoutSubviews();
+ base.Layout();
+ }
+
+ protected override void Dispose(bool disposing)
+ {
+ if (disposing)
+ {
+ IVisualElementRenderer renderer;
+ if (_rendererRef != null && _rendererRef.TryGetTarget(out renderer) && renderer.Element != null)
+ {
+ Platform.DisposeModelAndChildrenRenderers(renderer.Element);
+
+ _rendererRef = null;
+ }
+ }
+
+ base.Dispose(disposing);
+ }
+
+ void LayoutSubviews()
+ {
+ var contentFrame = Frame;
+ var view = ViewCell.View;
+
+ Xamarin.Forms.Layout.LayoutChildIntoBoundingRegion(view, contentFrame.ToRectangle());
+
+ if (_rendererRef == null)
+ return;
+
+ IVisualElementRenderer renderer;
+ if (_rendererRef.TryGetTarget(out renderer))
+ renderer.NativeView.Frame = view.Bounds.ToRectangleF();
+ }
+
+ IVisualElementRenderer GetNewRenderer()
+ {
+ var newRenderer = Platform.CreateRenderer(_viewCell.View);
+ _rendererRef = new WeakReference<IVisualElementRenderer>(newRenderer);
+ AddSubview(newRenderer.NativeView);
+ return newRenderer;
+ }
+
+ void UpdateCell(ViewCell cell)
+ {
+ ICellController cellController = _viewCell;
+ if (cellController != null)
+ Device.BeginInvokeOnMainThread(cellController.SendDisappearing);
+
+ _viewCell = cell;
+ cellController = cell;
+
+ Device.BeginInvokeOnMainThread(cellController.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 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
+ Platform.DisposeRendererAndChildren(renderer);
+ renderer = GetNewRenderer();
+ }
+ }
+
+ Platform.SetRenderer(_viewCell.View, renderer);
+ }
+ }
+} \ No newline at end of file
diff --git a/Xamarin.Forms.Platform.MacOS/Cells/ViewCellRenderer.cs b/Xamarin.Forms.Platform.MacOS/Cells/ViewCellRenderer.cs
new file mode 100644
index 00000000..8b345181
--- /dev/null
+++ b/Xamarin.Forms.Platform.MacOS/Cells/ViewCellRenderer.cs
@@ -0,0 +1,46 @@
+using System.ComponentModel;
+using AppKit;
+
+// ReSharper disable UnusedParameter.Local
+
+namespace Xamarin.Forms.Platform.MacOS
+{
+ public class ViewCellRenderer : CellRenderer
+ {
+ public override NSView GetCell(Cell item, NSView reusableView, NSTableView tv)
+ {
+ var viewCell = (ViewCell)item;
+
+ var cell = reusableView as ViewCellNSView;
+ if (cell == null)
+ cell = new ViewCellNSView();
+ else
+ cell.ViewCell.PropertyChanged -= ViewCellPropertyChanged;
+
+ viewCell.PropertyChanged += ViewCellPropertyChanged;
+ cell.ViewCell = viewCell;
+
+ SetRealCell(item, cell);
+
+ WireUpForceUpdateSizeRequested(item, cell, tv);
+
+ UpdateBackground(cell, item);
+ UpdateIsEnabled(cell, viewCell);
+ return cell;
+ }
+
+ static void UpdateIsEnabled(ViewCellNSView cell, ViewCell viewCell)
+ {
+ //TODO: Implement IsEnabled on ViewCell
+ }
+
+ static void ViewCellPropertyChanged(object sender, PropertyChangedEventArgs e)
+ {
+ var viewCell = (ViewCell)sender;
+ var realCell = (ViewCellNSView)GetRealCell(viewCell);
+
+ if (e.PropertyName == Cell.IsEnabledProperty.PropertyName)
+ UpdateIsEnabled(realCell, viewCell);
+ }
+ }
+} \ No newline at end of file