mirror of
https://github.com/SK-la/osu-framework.git
synced 2026-03-15 03:20:30 +00:00
Add support for providing system file selector on iOS
This commit is contained in:
71
osu.Framework.iOS/IOSFileSelector.cs
Normal file
71
osu.Framework.iOS/IOSFileSelector.cs
Normal file
@@ -0,0 +1,71 @@
|
||||
// Copyright (c) ppy Pty Ltd <contact@ppy.sh>. Licensed under the MIT Licence.
|
||||
// See the LICENCE file in the repository root for full licence text.
|
||||
|
||||
using System;
|
||||
using System.IO;
|
||||
using System.Runtime.Versioning;
|
||||
using Foundation;
|
||||
using osu.Framework.Platform;
|
||||
using UIKit;
|
||||
using UniformTypeIdentifiers;
|
||||
|
||||
namespace osu.Framework.iOS
|
||||
{
|
||||
[SupportedOSPlatform("ios14.0")]
|
||||
public class IOSFileSelector : UIDocumentPickerDelegate, ISystemFileSelector
|
||||
{
|
||||
public event Action<FileInfo>? Selected;
|
||||
|
||||
private readonly UIWindow window;
|
||||
|
||||
private readonly UIDocumentPickerViewController viewController;
|
||||
|
||||
public IOSFileSelector(UIWindow window, string[] allowedExtensions)
|
||||
{
|
||||
this.window = window;
|
||||
|
||||
UTType[] utTypes;
|
||||
|
||||
if (allowedExtensions.Length == 0)
|
||||
utTypes = new[] { UTTypes.Data };
|
||||
else
|
||||
{
|
||||
utTypes = new UTType[allowedExtensions.Length];
|
||||
|
||||
for (int i = 0; i < allowedExtensions.Length; i++)
|
||||
{
|
||||
string extension = allowedExtensions[i];
|
||||
|
||||
var type = UTType.CreateFromExtension(extension.Replace(".", string.Empty));
|
||||
if (type == null)
|
||||
throw new InvalidOperationException($"System failed to recognise extension \"{extension}\" when preparing the file selector.\n");
|
||||
|
||||
utTypes[i] = type;
|
||||
}
|
||||
}
|
||||
|
||||
// files must be provided as copies, as they may be originally located in places that cannot be accessed freely (aka. iCloud Drive).
|
||||
// we can acquire access to those files via startAccessingSecurityScopedResource but we must know when the game has finished using them.
|
||||
// todo: refactor FileSelector/DirectorySelector to be aware when the game finished using a file/directory.
|
||||
viewController = new UIDocumentPickerViewController(utTypes, true);
|
||||
viewController.Delegate = this;
|
||||
}
|
||||
|
||||
public void Present()
|
||||
{
|
||||
UIApplication.SharedApplication.InvokeOnMainThread(() =>
|
||||
{
|
||||
window.RootViewController!.PresentViewController(viewController, true, null);
|
||||
});
|
||||
}
|
||||
|
||||
public override void DidPickDocument(UIDocumentPickerViewController controller, NSUrl url)
|
||||
=> Selected?.Invoke(new FileInfo(url.Path!));
|
||||
|
||||
protected override void Dispose(bool disposing)
|
||||
{
|
||||
viewController.Dispose();
|
||||
base.Dispose(disposing);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -23,6 +23,8 @@ namespace osu.Framework.iOS
|
||||
{
|
||||
public class IOSGameHost : SDLGameHost
|
||||
{
|
||||
private IOSWindow iosWindow => (IOSWindow)Window;
|
||||
|
||||
public IOSGameHost()
|
||||
: base(string.Empty)
|
||||
{
|
||||
@@ -76,6 +78,23 @@ namespace osu.Framework.iOS
|
||||
public override VideoDecoder CreateVideoDecoder(Stream stream)
|
||||
=> new IOSVideoDecoder(Renderer, stream);
|
||||
|
||||
public override ISystemFileSelector? CreateSystemFileSelector(string[] allowedExtensions)
|
||||
{
|
||||
IOSFileSelector? selector = null;
|
||||
|
||||
UIApplication.SharedApplication.InvokeOnMainThread(() =>
|
||||
{
|
||||
// creating UIDocumentPickerViewController programmatically is only supported on iOS 14.0+.
|
||||
// on lower versions, return null and fall back to our normal file selector display.
|
||||
if (!OperatingSystem.IsIOSVersionAtLeast(14))
|
||||
return;
|
||||
|
||||
selector = new IOSFileSelector(iosWindow.UIWindow, allowedExtensions);
|
||||
});
|
||||
|
||||
return selector;
|
||||
}
|
||||
|
||||
public override IEnumerable<KeyBinding> PlatformKeyBindings => MacOSGameHost.KeyBindings;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -18,7 +18,9 @@ namespace osu.Framework.iOS
|
||||
{
|
||||
internal class IOSWindow : SDL3MobileWindow
|
||||
{
|
||||
private UIWindow? window;
|
||||
private UIWindow? uiWindow;
|
||||
|
||||
public UIWindow UIWindow => uiWindow!;
|
||||
|
||||
public override Size Size
|
||||
{
|
||||
@@ -27,7 +29,7 @@ namespace osu.Framework.iOS
|
||||
{
|
||||
base.Size = value;
|
||||
|
||||
if (window != null)
|
||||
if (uiWindow != null)
|
||||
updateSafeArea();
|
||||
}
|
||||
}
|
||||
@@ -43,7 +45,7 @@ namespace osu.Framework.iOS
|
||||
|
||||
base.Create();
|
||||
|
||||
window = Runtime.GetNSObject<UIWindow>(WindowHandle);
|
||||
uiWindow = Runtime.GetNSObject<UIWindow>(WindowHandle)!;
|
||||
updateSafeArea();
|
||||
}
|
||||
|
||||
@@ -73,14 +75,14 @@ namespace osu.Framework.iOS
|
||||
|
||||
private void updateSafeArea()
|
||||
{
|
||||
Debug.Assert(window != null);
|
||||
Debug.Assert(uiWindow != null);
|
||||
|
||||
SafeAreaPadding.Value = new MarginPadding
|
||||
{
|
||||
Top = (float)window.SafeAreaInsets.Top * Scale,
|
||||
Left = (float)window.SafeAreaInsets.Left * Scale,
|
||||
Bottom = (float)window.SafeAreaInsets.Bottom * Scale,
|
||||
Right = (float)window.SafeAreaInsets.Right * Scale,
|
||||
Top = (float)uiWindow.SafeAreaInsets.Top * Scale,
|
||||
Left = (float)uiWindow.SafeAreaInsets.Left * Scale,
|
||||
Bottom = (float)uiWindow.SafeAreaInsets.Bottom * Scale,
|
||||
Right = (float)uiWindow.SafeAreaInsets.Right * Scale,
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user