diff --git a/azure-pipelines.yml b/azure-pipelines.yml
index a306e22355..3f918d8bc2 100644
--- a/azure-pipelines.yml
+++ b/azure-pipelines.yml
@@ -171,7 +171,7 @@ jobs:
displayName: 'Install Workloads'
inputs:
script: |
- dotnet workload install android ios wasm-tools wasm-experimental
+ dotnet workload install android ios tvos wasm-tools wasm-experimental
- task: PowerShell@2
displayName: 'Install Tizen Workload'
diff --git a/samples/ControlCatalog.iOS/ControlCatalog.iOS.csproj b/samples/ControlCatalog.iOS/ControlCatalog.iOS.csproj
index 9826824c3c..dc1309150c 100644
--- a/samples/ControlCatalog.iOS/ControlCatalog.iOS.csproj
+++ b/samples/ControlCatalog.iOS/ControlCatalog.iOS.csproj
@@ -3,6 +3,7 @@
Exe
manual
net7.0-ios
+
13.0
diff --git a/samples/ControlCatalog.iOS/EmbedSample.iOS.cs b/samples/ControlCatalog.iOS/EmbedSample.iOS.cs
index ad86d2b578..8fe72d1cff 100644
--- a/samples/ControlCatalog.iOS/EmbedSample.iOS.cs
+++ b/samples/ControlCatalog.iOS/EmbedSample.iOS.cs
@@ -3,7 +3,6 @@ using Avalonia.Platform;
using CoreGraphics;
using Foundation;
using UIKit;
-using WebKit;
using Avalonia.iOS;
using ControlCatalog.Pages;
@@ -13,14 +12,16 @@ public class EmbedSampleIOS : INativeDemoControl
{
public IPlatformHandle CreateControl(bool isSecond, IPlatformHandle parent, Func createDefault)
{
+#if !TVOS
if (isSecond)
{
- var webView = new WKWebView(CGRect.Empty, new WKWebViewConfiguration());
+ var webView = new WebKit.WKWebView(CGRect.Empty, new WebKit.WKWebViewConfiguration());
webView.LoadRequest(new NSUrlRequest(new NSUrl("https://www.apple.com/")));
return new UIViewControlHandle(webView);
}
else
+#endif
{
var button = new UIButton();
var clickCount = 0;
diff --git a/samples/ControlCatalog.iOS/Info.plist b/samples/ControlCatalog.iOS/Info.plist
index 1dd4416c28..d97c088652 100644
--- a/samples/ControlCatalog.iOS/Info.plist
+++ b/samples/ControlCatalog.iOS/Info.plist
@@ -18,13 +18,14 @@
1
2
+ 3
+ UIRequiredDeviceCapabilities
+
+ arm64
+
UILaunchStoryboardName
LaunchScreen
- UIRequiredDeviceCapabilities
-
- armv7
-
UISupportedInterfaceOrientations
UIInterfaceOrientationPortrait
diff --git a/src/Avalonia.Base/Compatibility/OperatingSystem.cs b/src/Avalonia.Base/Compatibility/OperatingSystem.cs
index 838f7da8b2..ad5fe0246a 100644
--- a/src/Avalonia.Base/Compatibility/OperatingSystem.cs
+++ b/src/Avalonia.Base/Compatibility/OperatingSystem.cs
@@ -8,18 +8,24 @@ namespace Avalonia.Compatibility
#if NET6_0_OR_GREATER
public static bool IsWindows() => OperatingSystem.IsWindows();
public static bool IsMacOS() => OperatingSystem.IsMacOS();
+ public static bool IsMacCatalyst() => OperatingSystem.IsMacCatalyst();
public static bool IsLinux() => OperatingSystem.IsLinux();
+ public static bool IsFreeBSD() => OperatingSystem.IsFreeBSD();
public static bool IsAndroid() => OperatingSystem.IsAndroid();
public static bool IsIOS() => OperatingSystem.IsIOS();
+ public static bool IsTvOS() => OperatingSystem.IsTvOS();
public static bool IsBrowser() => OperatingSystem.IsBrowser();
public static bool IsOSPlatform(string platform) => OperatingSystem.IsOSPlatform(platform);
#else
public static bool IsWindows() => RuntimeInformation.IsOSPlatform(OSPlatform.Windows);
public static bool IsMacOS() => RuntimeInformation.IsOSPlatform(OSPlatform.OSX);
public static bool IsLinux() => RuntimeInformation.IsOSPlatform(OSPlatform.Linux);
- public static bool IsAndroid() => IsOSPlatform("ANDROID");
- public static bool IsIOS() => IsOSPlatform("IOS");
- public static bool IsBrowser() => IsOSPlatform("BROWSER");
+ public static bool IsFreeBSD() => false;
+ public static bool IsAndroid() => false;
+ public static bool IsIOS() => false;
+ public static bool IsMacCatalyst() => false;
+ public static bool IsTvOS() => false;
+ public static bool IsBrowser() => false;
public static bool IsOSPlatform(string platform) => RuntimeInformation.IsOSPlatform(OSPlatform.Create(platform));
#endif
}
diff --git a/src/Avalonia.Base/Platform/StandardRuntimePlatform.cs b/src/Avalonia.Base/Platform/StandardRuntimePlatform.cs
index 7a5c92c774..b72e10c831 100644
--- a/src/Avalonia.Base/Platform/StandardRuntimePlatform.cs
+++ b/src/Avalonia.Base/Platform/StandardRuntimePlatform.cs
@@ -1,20 +1,18 @@
-using System;
-using System.Threading;
using Avalonia.Compatibility;
using Avalonia.Metadata;
-using Avalonia.Platform.Internal;
namespace Avalonia.Platform
{
[PrivateApi]
public class StandardRuntimePlatform : IRuntimePlatform
{
- private static readonly RuntimePlatformInfo s_info = new()
+ public virtual RuntimePlatformInfo GetRuntimeInfo() => new()
{
- IsDesktop = OperatingSystemEx.IsWindows() || OperatingSystemEx.IsMacOS() || OperatingSystemEx.IsLinux(),
- IsMobile = OperatingSystemEx.IsAndroid() || OperatingSystemEx.IsIOS()
+ IsDesktop = OperatingSystemEx.IsWindows()
+ || OperatingSystemEx.IsMacOS() || OperatingSystemEx.IsMacCatalyst()
+ || OperatingSystemEx.IsLinux() || OperatingSystemEx.IsFreeBSD(),
+ IsMobile = OperatingSystemEx.IsAndroid() || (OperatingSystemEx.IsIOS() && !OperatingSystemEx.IsMacCatalyst()),
+ IsTV = OperatingSystemEx.IsTvOS()
};
-
- public virtual RuntimePlatformInfo GetRuntimeInfo() => s_info;
}
}
diff --git a/src/Avalonia.Controls/ApplicationLifetimes/IActivatableApplicationLifetime.cs b/src/Avalonia.Controls/ApplicationLifetimes/IActivatableApplicationLifetime.cs
index fbdfe3aa7d..b04397f1c6 100644
--- a/src/Avalonia.Controls/ApplicationLifetimes/IActivatableApplicationLifetime.cs
+++ b/src/Avalonia.Controls/ApplicationLifetimes/IActivatableApplicationLifetime.cs
@@ -11,13 +11,13 @@ public interface IActivatableApplicationLifetime
/// An event that is raised when the application is Activated for various reasons
/// as described by the enumeration.
///
- event EventHandler Activated;
+ event EventHandler? Activated;
///
/// An event that is raised when the application is Deactivated for various reasons
/// as described by the enumeration.
///
- event EventHandler Deactivated;
+ event EventHandler? Deactivated;
///
/// Tells the application that it should attempt to leave its background state.
diff --git a/src/Skia/Avalonia.Skia/Gpu/Metal/SkiaMetalApi.cs b/src/Skia/Avalonia.Skia/Gpu/Metal/SkiaMetalApi.cs
index 3022f92ec5..c1c0a74542 100644
--- a/src/Skia/Avalonia.Skia/Gpu/Metal/SkiaMetalApi.cs
+++ b/src/Skia/Avalonia.Skia/Gpu/Metal/SkiaMetalApi.cs
@@ -26,7 +26,12 @@ internal unsafe class SkiaMetalApi
// Make sure that skia is loaded
GC.KeepAlive(new SKPaint());
- var dll = NativeLibraryEx.Load("libSkiaSharp", typeof(SKPaint).Assembly);
+ // https://github.com/mono/SkiaSharp/blob/25e70a390e2128e5a54d28795365bf9fdaa7161c/binding/SkiaSharp/SkiaApi.cs#L9-L13
+ // Note, IsIOS also returns true on MacCatalyst.
+ var libSkiaSharpPath = OperatingSystemEx.IsIOS() || OperatingSystemEx.IsTvOS() ?
+ "@rpath/libSkiaSharp.framework/libSkiaSharp" :
+ "libSkiaSharp";
+ var dll = NativeLibraryEx.Load(libSkiaSharpPath, typeof(SKPaint).Assembly);
IntPtr address;
@@ -75,7 +80,7 @@ internal unsafe class SkiaMetalApi
var context = _gr_direct_context_make_metal_with_options(device, queue, pOptions);
Marshal.FreeHGlobal(pOptions);
if (context == IntPtr.Zero)
- throw new ArgumentException();
+ throw new InvalidOperationException("Unable to create GRContext from Metal device.");
return (GRContext)_contextCtor.Invoke(new object[] { context, true });
}
diff --git a/src/iOS/Avalonia.iOS/Avalonia.iOS.csproj b/src/iOS/Avalonia.iOS/Avalonia.iOS.csproj
index eb4938c13f..b9da0377fd 100644
--- a/src/iOS/Avalonia.iOS/Avalonia.iOS.csproj
+++ b/src/iOS/Avalonia.iOS/Avalonia.iOS.csproj
@@ -1,15 +1,33 @@
- net7.0-ios16.0
- 13.0
+ net7.0-ios16.0;net7.0-tvos
+ 13.0
+ 13.0
+
+ 13.1
true
true
+
+
+
+ $(NoWarn);CA1416
+
+
+
+
+
+
+
+
+
+
+
diff --git a/src/iOS/Avalonia.iOS/AvaloniaAppDelegate.cs b/src/iOS/Avalonia.iOS/AvaloniaAppDelegate.cs
index ecb9e56aa9..e4ec20bbbd 100644
--- a/src/iOS/Avalonia.iOS/AvaloniaAppDelegate.cs
+++ b/src/iOS/Avalonia.iOS/AvaloniaAppDelegate.cs
@@ -15,7 +15,7 @@ namespace Avalonia.iOS
public class AvaloniaAppDelegate : UIResponder, IUIApplicationDelegate, IAvaloniaAppDelegate
where TApp : Application, new()
{
- private EventHandler _onActivated, _onDeactivated;
+ private EventHandler? _onActivated, _onDeactivated;
public AvaloniaAppDelegate()
{
@@ -37,7 +37,7 @@ namespace Avalonia.iOS
protected virtual AppBuilder CustomizeAppBuilder(AppBuilder builder) => builder;
[Export("window")]
- public UIWindow Window { get; set; }
+ public UIWindow? Window { get; set; }
[Export("application:didFinishLaunchingWithOptions:")]
public bool FinishedLaunching(UIApplication application, NSDictionary launchOptions)
@@ -64,7 +64,7 @@ namespace Avalonia.iOS
builder.SetupWithLifetime(lifetime);
- Window.MakeKeyAndVisible();
+ Window!.MakeKeyAndVisible();
return true;
}
diff --git a/src/iOS/Avalonia.iOS/AvaloniaView.Text.cs b/src/iOS/Avalonia.iOS/AvaloniaView.Text.cs
index 0b0e169e8a..dbdddd2cc5 100644
--- a/src/iOS/Avalonia.iOS/AvaloniaView.Text.cs
+++ b/src/iOS/Avalonia.iOS/AvaloniaView.Text.cs
@@ -1,4 +1,3 @@
-#nullable enable
using Avalonia.Input.TextInput;
using UIKit;
diff --git a/src/iOS/Avalonia.iOS/AvaloniaView.cs b/src/iOS/Avalonia.iOS/AvaloniaView.cs
index a51d3f2b28..26ff0f8e07 100644
--- a/src/iOS/Avalonia.iOS/AvaloniaView.cs
+++ b/src/iOS/Avalonia.iOS/AvaloniaView.cs
@@ -1,7 +1,6 @@
-#nullable enable
-
using System;
using System.Collections.Generic;
+using System.Diagnostics.CodeAnalysis;
using System.Runtime.Versioning;
using Avalonia.Controls;
using Avalonia.Controls.Embedding;
@@ -12,19 +11,20 @@ using Avalonia.Input;
using Avalonia.Input.Platform;
using Avalonia.Input.Raw;
using Avalonia.Input.TextInput;
-using Avalonia.iOS.Storage;
using Avalonia.Platform;
using Avalonia.Platform.Storage;
using Avalonia.Rendering.Composition;
using CoreAnimation;
using Foundation;
using ObjCRuntime;
-using OpenGLES;
using UIKit;
using IInsetsManager = Avalonia.Controls.Platform.IInsetsManager;
namespace Avalonia.iOS
{
+ ///
+ /// Root view container for Avalonia content, that can be embedded into iOS visual tree.
+ ///
public partial class AvaloniaView : UIView, ITextInputMethodImpl
{
internal IInputRoot InputRoot
@@ -32,38 +32,74 @@ namespace Avalonia.iOS
private readonly TopLevelImpl _topLevelImpl;
private readonly EmbeddableControlRoot _topLevel;
- private readonly TouchHandler _touches;
+ private readonly InputHandler _input;
private TextInputMethodClient? _client;
private IAvaloniaViewController? _controller;
private IInputRoot? _inputRoot;
+ private Metal.MetalRenderTarget? _currentRenderTarget;
+ private (PixelSize size, double scaling) _latestLayoutProps;
public AvaloniaView()
{
_topLevelImpl = new TopLevelImpl(this);
- _touches = new TouchHandler(this, _topLevelImpl);
+ _input = new InputHandler(this, _topLevelImpl);
_topLevel = new EmbeddableControlRoot(_topLevelImpl);
_topLevel.Prepare();
_topLevel.StartRendering();
- InitEagl();
- MultipleTouchEnabled = true;
+ InitLayerSurface();
+
+ // Remote touch handling
+ if (OperatingSystem.IsTvOS())
+ {
+ AddGestureRecognizer(new UISwipeGestureRecognizer(_input.Handle)
+ {
+ Direction = UISwipeGestureRecognizerDirection.Up
+ });
+ AddGestureRecognizer(new UISwipeGestureRecognizer(_input.Handle)
+ {
+ Direction = UISwipeGestureRecognizerDirection.Right
+ });
+ AddGestureRecognizer(new UISwipeGestureRecognizer(_input.Handle)
+ {
+ Direction = UISwipeGestureRecognizerDirection.Down
+ });
+ AddGestureRecognizer(new UISwipeGestureRecognizer(_input.Handle)
+ {
+ Direction = UISwipeGestureRecognizerDirection.Left
+ });
+ }
+ else if (OperatingSystem.IsIOS() || OperatingSystem.IsMacCatalyst())
+ {
+#if !TVOS
+ MultipleTouchEnabled = true;
+#endif
+ }
}
- [ObsoletedOSPlatform("ios12.0", "Use 'Metal' instead.")]
- [SupportedOSPlatform("ios")]
- [UnsupportedOSPlatform("maccatalyst")]
- private void InitEagl()
+ [SuppressMessage("Interoperability", "CA1422:Validate platform compatibility")]
+ private void InitLayerSurface()
{
- var l = (CAEAGLLayer)Layer;
+ var l = Layer;
l.ContentsScale = UIScreen.MainScreen.Scale;
l.Opaque = true;
- l.DrawableProperties = new NSDictionary(
- EAGLDrawableProperty.RetainedBacking, false,
- EAGLDrawableProperty.ColorFormat, EAGLColorFormat.RGBA8
- );
- _topLevelImpl.Surfaces = new[] { new EaglLayerSurface(l) };
+#if !MACCATALYST
+ if (l is CAEAGLLayer eaglLayer)
+ {
+ eaglLayer.DrawableProperties = new NSDictionary(
+ OpenGLES.EAGLDrawableProperty.RetainedBacking, false,
+ OpenGLES.EAGLDrawableProperty.ColorFormat, OpenGLES.EAGLColorFormat.RGBA8
+ );
+ _topLevelImpl.Surfaces = new[] { new Eagl.EaglLayerSurface(eaglLayer) };
+ }
+ else
+#endif
+ if (l is CAMetalLayer metalLayer)
+ {
+ _topLevelImpl.Surfaces = new[] { new Metal.MetalPlatformSurface(metalLayer, this) };
+ }
}
///
@@ -73,6 +109,12 @@ namespace Avalonia.iOS
public override bool CanResignFirstResponder => true;
///
+ [ObsoletedOSPlatform("ios17.0", "Use the 'UITraitChangeObservable' protocol instead.")]
+ [ObsoletedOSPlatform("maccatalyst17.0", "Use the 'UITraitChangeObservable' protocol instead.")]
+ [ObsoletedOSPlatform("tvos17.0", "Use the 'UITraitChangeObservable' protocol instead.")]
+ [SupportedOSPlatform("ios")]
+ [SupportedOSPlatform("tvos")]
+ [SupportedOSPlatform("maccatalyst")]
public override void TraitCollectionDidChange(UITraitCollection? previousTraitCollection)
{
base.TraitCollectionDidChange(previousTraitCollection);
@@ -101,9 +143,10 @@ namespace Avalonia.iOS
{
private readonly AvaloniaView _view;
private readonly INativeControlHostImpl _nativeControlHost;
- private readonly IStorageProvider _storageProvider;
internal readonly InsetsManager _insetsManager;
- private readonly ClipboardImpl _clipboard;
+ private readonly IStorageProvider? _storageProvider;
+ private readonly IClipboard? _clipboard;
+ private readonly IInputPane? _inputPane;
private IDisposable? _paddingInsets;
public AvaloniaView View => _view;
@@ -112,8 +155,16 @@ namespace Avalonia.iOS
{
_view = view;
_nativeControlHost = new NativeControlHostImpl(view);
- _storageProvider = new IOSStorageProvider(view);
- _insetsManager = new InsetsManager(view);
+#if TVOS
+ _storageProvider = null;
+ _clipboard = null;
+ _inputPane = null;
+#else
+ _storageProvider = new Storage.IOSStorageProvider(view);
+ _clipboard = new ClipboardImpl();
+ _inputPane = UIKitInputPane.Instance;
+#endif
+ _insetsManager = new InsetsManager();
_insetsManager.DisplayEdgeToEdgeChanged += (_, edgeToEdge) =>
{
// iOS doesn't add any paddings/margins to the application by itself.
@@ -128,7 +179,6 @@ namespace Avalonia.iOS
BindingPriority.Style); // lower priority, so it can be redefined by user
}
};
- _clipboard = new ClipboardImpl();
}
public void Dispose()
@@ -136,7 +186,8 @@ namespace Avalonia.iOS
// No-op
}
- public Compositor Compositor => Platform.Compositor;
+ public Compositor Compositor => Platform.Compositor
+ ?? throw new InvalidOperationException("iOS backend wasn't initialized. Make sure UseiOS was called.");
public void Invalidate(Rect rect)
{
@@ -185,8 +236,11 @@ namespace Avalonia.iOS
public void SetFrameThemeVariant(PlatformThemeVariant themeVariant)
{
+#if !TVOS
// TODO adjust status bar depending on full screen mode.
- if (OperatingSystem.IsIOSVersionAtLeast(13) && _view._controller is not null)
+ if ((OperatingSystem.IsIOSVersionAtLeast(13)
+ || OperatingSystem.IsMacCatalyst())
+ && _view._controller is not null)
{
_view._controller.PreferredStatusBarStyle = themeVariant switch
{
@@ -195,6 +249,7 @@ namespace Avalonia.iOS
_ => UIStatusBarStyle.Default
};
}
+#endif
}
public AcrylicPlatformCompensationLevels AcrylicCompensationLevels { get; } =
@@ -202,11 +257,6 @@ namespace Avalonia.iOS
public object? TryGetFeature(Type featureType)
{
- if (featureType == typeof(IStorageProvider))
- {
- return _storageProvider;
- }
-
if (featureType == typeof(ITextInputMethodImpl))
{
return _view;
@@ -227,9 +277,14 @@ namespace Avalonia.iOS
return _clipboard;
}
+ if (featureType == typeof(IStorageProvider))
+ {
+ return _storageProvider;
+ }
+
if (featureType == typeof(IInputPane))
{
- return UIKitInputPane.Instance;
+ return _inputPane;
}
return null;
@@ -239,20 +294,77 @@ namespace Avalonia.iOS
[Export("layerClass")]
public static Class LayerClass()
{
- return new Class(typeof(CAEAGLLayer));
+#if !MACCATALYST
+ if (Platform.Graphics is Eagl.EaglPlatformGraphics)
+ {
+ return new Class(typeof(CAEAGLLayer));
+ }
+ else
+#endif
+ {
+ return new Class(typeof(CAMetalLayer));
+ }
}
- public override void TouchesBegan(NSSet touches, UIEvent? evt) => _touches.Handle(touches, evt);
+ ///
+ public override void TouchesBegan(NSSet touches, UIEvent? evt) => _input.Handle(touches, evt);
+
+ ///
+ public override void TouchesMoved(NSSet touches, UIEvent? evt) => _input.Handle(touches, evt);
- public override void TouchesMoved(NSSet touches, UIEvent? evt) => _touches.Handle(touches, evt);
+ ///
+ public override void TouchesEnded(NSSet touches, UIEvent? evt) => _input.Handle(touches, evt);
- public override void TouchesEnded(NSSet touches, UIEvent? evt) => _touches.Handle(touches, evt);
+ ///
+ public override void TouchesCancelled(NSSet touches, UIEvent? evt) => _input.Handle(touches, evt);
- public override void TouchesCancelled(NSSet touches, UIEvent? evt) => _touches.Handle(touches, evt);
+ ///
+ public override void PressesBegan(NSSet presses, UIPressesEvent evt)
+ {
+ if (!_input.Handle(presses, evt))
+ {
+ base.PressesBegan(presses, evt);
+ }
+ }
+ ///
+ public override void PressesChanged(NSSet presses, UIPressesEvent evt)
+ {
+ if (!_input.Handle(presses, evt))
+ {
+ base.PressesBegan(presses, evt);
+ }
+ }
+
+ ///
+ public override void PressesEnded(NSSet presses, UIPressesEvent evt)
+ {
+ if (!_input.Handle(presses, evt))
+ {
+ base.PressesEnded(presses, evt);
+ }
+ }
+
+ ///
+ public override void PressesCancelled(NSSet presses, UIPressesEvent evt)
+ {
+ if (!_input.Handle(presses, evt))
+ {
+ base.PressesCancelled(presses, evt);
+ }
+ }
+
+ ///
public override void LayoutSubviews()
{
_topLevelImpl.Resized?.Invoke(_topLevelImpl.ClientSize, WindowResizeReason.Layout);
+ var scaling = (double)ContentScaleFactor;
+ _latestLayoutProps = (new PixelSize((int)(Bounds.Width * scaling), (int)(Bounds.Height * scaling)), scaling);
+ if (_currentRenderTarget is not null)
+ {
+ _currentRenderTarget.PendingLayout = _latestLayoutProps;
+ }
+
base.LayoutSubviews();
}
@@ -261,5 +373,11 @@ namespace Avalonia.iOS
get => (Control?)_topLevel.Content;
set => _topLevel.Content = value;
}
+
+ internal void SetRenderTarget(Metal.MetalRenderTarget target)
+ {
+ _currentRenderTarget = target;
+ _currentRenderTarget.PendingLayout = _latestLayoutProps;
+ }
}
}
diff --git a/src/iOS/Avalonia.iOS/ClipboardImpl.cs b/src/iOS/Avalonia.iOS/ClipboardImpl.cs
index 150f3424e3..8c6cadee0e 100644
--- a/src/iOS/Avalonia.iOS/ClipboardImpl.cs
+++ b/src/iOS/Avalonia.iOS/ClipboardImpl.cs
@@ -1,19 +1,21 @@
using System;
+using System.Collections.Generic;
using System.Threading.Tasks;
using Avalonia.Input;
using Avalonia.Input.Platform;
+using Foundation;
using UIKit;
namespace Avalonia.iOS
{
internal class ClipboardImpl : IClipboard
{
- public Task GetTextAsync()
+ public Task GetTextAsync()
{
return Task.FromResult(UIPasteboard.General.String);
}
- public Task SetTextAsync(string text)
+ public Task SetTextAsync(string? text)
{
UIPasteboard.General.String = text;
return Task.CompletedTask;
@@ -21,14 +23,39 @@ namespace Avalonia.iOS
public Task ClearAsync()
{
- UIPasteboard.General.String = "";
+ UIPasteboard.General.Items = Array.Empty();
return Task.CompletedTask;
}
- public Task SetDataObjectAsync(IDataObject data) => Task.CompletedTask;
+ public Task SetDataObjectAsync(IDataObject data)
+ {
+ if (data.Contains(DataFormats.Text))
+ {
+ UIPasteboard.General.String = data.GetText();
+ }
+
+ return Task.CompletedTask;
+ }
+
+ public Task GetFormatsAsync()
+ {
+ var formats = new List();
+ if (UIPasteboard.General.HasStrings)
+ {
+ formats.Add(DataFormats.Text);
+ }
- public Task GetFormatsAsync() => Task.FromResult(Array.Empty());
+ return Task.FromResult(formats.ToArray());
+ }
- public Task