@ -0,0 +1,9 @@ |
|||
using Avalonia.iOS; |
|||
|
|||
namespace NativeEmbedSample.iOS; |
|||
|
|||
[Register("AppDelegate")] |
|||
public partial class AppDelegate : AvaloniaAppDelegate<App> |
|||
{ |
|||
|
|||
} |
|||
@ -0,0 +1,117 @@ |
|||
{ |
|||
"images": [ |
|||
{ |
|||
"scale": "2x", |
|||
"size": "20x20", |
|||
"idiom": "iphone", |
|||
"filename": "Icon40.png" |
|||
}, |
|||
{ |
|||
"scale": "3x", |
|||
"size": "20x20", |
|||
"idiom": "iphone", |
|||
"filename": "Icon60.png" |
|||
}, |
|||
{ |
|||
"scale": "2x", |
|||
"size": "29x29", |
|||
"idiom": "iphone", |
|||
"filename": "Icon58.png" |
|||
}, |
|||
{ |
|||
"scale": "3x", |
|||
"size": "29x29", |
|||
"idiom": "iphone", |
|||
"filename": "Icon87.png" |
|||
}, |
|||
{ |
|||
"scale": "2x", |
|||
"size": "40x40", |
|||
"idiom": "iphone", |
|||
"filename": "Icon80.png" |
|||
}, |
|||
{ |
|||
"scale": "3x", |
|||
"size": "40x40", |
|||
"idiom": "iphone", |
|||
"filename": "Icon120.png" |
|||
}, |
|||
{ |
|||
"scale": "2x", |
|||
"size": "60x60", |
|||
"idiom": "iphone", |
|||
"filename": "Icon120.png" |
|||
}, |
|||
{ |
|||
"scale": "3x", |
|||
"size": "60x60", |
|||
"idiom": "iphone", |
|||
"filename": "Icon180.png" |
|||
}, |
|||
{ |
|||
"scale": "1x", |
|||
"size": "20x20", |
|||
"idiom": "ipad", |
|||
"filename": "Icon20.png" |
|||
}, |
|||
{ |
|||
"scale": "2x", |
|||
"size": "20x20", |
|||
"idiom": "ipad", |
|||
"filename": "Icon40.png" |
|||
}, |
|||
{ |
|||
"scale": "1x", |
|||
"size": "29x29", |
|||
"idiom": "ipad", |
|||
"filename": "Icon29.png" |
|||
}, |
|||
{ |
|||
"scale": "2x", |
|||
"size": "29x29", |
|||
"idiom": "ipad", |
|||
"filename": "Icon58.png" |
|||
}, |
|||
{ |
|||
"scale": "1x", |
|||
"size": "40x40", |
|||
"idiom": "ipad", |
|||
"filename": "Icon40.png" |
|||
}, |
|||
{ |
|||
"scale": "2x", |
|||
"size": "40x40", |
|||
"idiom": "ipad", |
|||
"filename": "Icon80.png" |
|||
}, |
|||
{ |
|||
"scale": "1x", |
|||
"size": "76x76", |
|||
"idiom": "ipad", |
|||
"filename": "Icon76.png" |
|||
}, |
|||
{ |
|||
"scale": "2x", |
|||
"size": "76x76", |
|||
"idiom": "ipad", |
|||
"filename": "Icon152.png" |
|||
}, |
|||
{ |
|||
"scale": "2x", |
|||
"size": "83.5x83.5", |
|||
"idiom": "ipad", |
|||
"filename": "Icon167.png" |
|||
}, |
|||
{ |
|||
"scale": "1x", |
|||
"size": "1024x1024", |
|||
"idiom": "ios-marketing", |
|||
"filename": "Icon1024.png" |
|||
} |
|||
], |
|||
"properties": {}, |
|||
"info": { |
|||
"version": 1, |
|||
"author": "xcode" |
|||
} |
|||
} |
|||
|
After Width: | Height: | Size: 69 KiB |
|
After Width: | Height: | Size: 3.7 KiB |
|
After Width: | Height: | Size: 4.6 KiB |
|
After Width: | Height: | Size: 4.6 KiB |
|
After Width: | Height: | Size: 5.1 KiB |
|
After Width: | Height: | Size: 1.3 KiB |
|
After Width: | Height: | Size: 845 B |
|
After Width: | Height: | Size: 1.1 KiB |
|
After Width: | Height: | Size: 1.7 KiB |
|
After Width: | Height: | Size: 2.5 KiB |
|
After Width: | Height: | Size: 2.3 KiB |
|
After Width: | Height: | Size: 2.4 KiB |
|
After Width: | Height: | Size: 2.7 KiB |
@ -0,0 +1,6 @@ |
|||
<?xml version="1.0" encoding="UTF-8"?> |
|||
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd"> |
|||
<plist version="1.0"> |
|||
<dict> |
|||
</dict> |
|||
</plist> |
|||
@ -0,0 +1,42 @@ |
|||
<?xml version="1.0" encoding="UTF-8"?> |
|||
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd"> |
|||
<plist version="1.0"> |
|||
<dict> |
|||
<key>CFBundleDisplayName</key> |
|||
<string>AvaloniaNative</string> |
|||
<key>CFBundleIdentifier</key> |
|||
<string>Avalonia.Native.Sample</string> |
|||
<key>CFBundleShortVersionString</key> |
|||
<string>1.0</string> |
|||
<key>CFBundleVersion</key> |
|||
<string>1.0</string> |
|||
<key>LSRequiresIPhoneOS</key> |
|||
<true/> |
|||
<key>UIDeviceFamily</key> |
|||
<array> |
|||
<integer>1</integer> |
|||
<integer>2</integer> |
|||
</array> |
|||
<key>UILaunchStoryboardName</key> |
|||
<string>LaunchScreen</string> |
|||
<key>UIRequiredDeviceCapabilities</key> |
|||
<array> |
|||
<string>armv7</string> |
|||
</array> |
|||
<key>UISupportedInterfaceOrientations</key> |
|||
<array> |
|||
<string>UIInterfaceOrientationPortrait</string> |
|||
<string>UIInterfaceOrientationLandscapeLeft</string> |
|||
<string>UIInterfaceOrientationLandscapeRight</string> |
|||
</array> |
|||
<key>UISupportedInterfaceOrientations~ipad</key> |
|||
<array> |
|||
<string>UIInterfaceOrientationPortrait</string> |
|||
<string>UIInterfaceOrientationPortraitUpsideDown</string> |
|||
<string>UIInterfaceOrientationLandscapeLeft</string> |
|||
<string>UIInterfaceOrientationLandscapeRight</string> |
|||
</array> |
|||
<key>XSAppIconAssets</key> |
|||
<string>Assets.xcassets/AppIcon.appiconset</string> |
|||
</dict> |
|||
</plist> |
|||
@ -0,0 +1,6 @@ |
|||
using NativeEmbedSample.iOS; |
|||
|
|||
// This is the main entry point of the application.
|
|||
// If you want to use a different Application Delegate class from "AppDelegate"
|
|||
// you can specify it here.
|
|||
UIApplication.Main(args, null, typeof(AppDelegate)); |
|||
@ -0,0 +1,16 @@ |
|||
<Project Sdk="Microsoft.NET.Sdk"> |
|||
<PropertyGroup> |
|||
<TargetFramework>net6.0-ios</TargetFramework> |
|||
<ProvisioningType>manual</ProvisioningType> |
|||
<OutputType>Exe</OutputType> |
|||
<Nullable>enable</Nullable> |
|||
<ImplicitUsings>true</ImplicitUsings> |
|||
<SupportedOSPlatformVersion>11.2</SupportedOSPlatformVersion> |
|||
<RuntimeIdentifier>iossimulator-x64</RuntimeIdentifier> |
|||
<!-- <RuntimeIdentifier>ios-arm64</RuntimeIdentifier>--> |
|||
</PropertyGroup> |
|||
<ItemGroup> |
|||
<ProjectReference Include="..\..\..\src\iOS\Avalonia.iOS\Avalonia.iOS.csproj" /> |
|||
<ProjectReference Include="..\NativeEmbedSample\NativeEmbedSample.csproj" /> |
|||
</ItemGroup> |
|||
</Project> |
|||
@ -0,0 +1,43 @@ |
|||
<?xml version="1.0" encoding="UTF-8" standalone="no"?> |
|||
<document type="com.apple.InterfaceBuilder3.CocoaTouch.XIB" version="3.0" toolsVersion="6214" systemVersion="14A314h" targetRuntime="iOS.CocoaTouch" propertyAccessControl="none" useAutolayout="YES" launchScreen="YES" useTraitCollections="YES"> |
|||
<dependencies> |
|||
<plugIn identifier="com.apple.InterfaceBuilder.IBCocoaTouchPlugin" version="6207" /> |
|||
<capability name="Constraints with non-1.0 multipliers" minToolsVersion="5.1" /> |
|||
</dependencies> |
|||
<objects> |
|||
<placeholder placeholderIdentifier="IBFilesOwner" id="-1" userLabel="File's Owner" /> |
|||
<placeholder placeholderIdentifier="IBFirstResponder" id="-2" customClass="UIResponder" /> |
|||
<view contentMode="scaleToFill" id="iN0-l3-epB"> |
|||
<rect key="frame" x="0.0" y="0.0" width="480" height="480" /> |
|||
<autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES" /> |
|||
<subviews> |
|||
<label opaque="NO" clipsSubviews="YES" userInteractionEnabled="NO" contentMode="left" horizontalHuggingPriority="251" verticalHuggingPriority="251" text=" Copyright (c) 2017 " textAlignment="center" lineBreakMode="tailTruncation" baselineAdjustment="alignBaselines" |
|||
minimumFontSize="9" translatesAutoresizingMaskIntoConstraints="NO" id="8ie-xW-0ye"> |
|||
<rect key="frame" x="20" y="439" width="441" height="21" /> |
|||
<fontDescription key="fontDescription" type="system" pointSize="17" /> |
|||
<color key="textColor" cocoaTouchSystemColor="darkTextColor" /> |
|||
<nil key="highlightedColor" /> |
|||
</label> |
|||
<label opaque="NO" clipsSubviews="YES" userInteractionEnabled="NO" contentMode="left" horizontalHuggingPriority="251" verticalHuggingPriority="251" text="NewApp" textAlignment="center" lineBreakMode="middleTruncation" baselineAdjustment="alignBaselines" |
|||
minimumFontSize="18" translatesAutoresizingMaskIntoConstraints="NO" id="kId-c2-rCX"> |
|||
<rect key="frame" x="20" y="140" width="441" height="43" /> |
|||
<fontDescription key="fontDescription" type="boldSystem" pointSize="36" /> |
|||
<color key="textColor" cocoaTouchSystemColor="darkTextColor" /> |
|||
<nil key="highlightedColor" /> |
|||
</label> |
|||
</subviews> |
|||
<color key="backgroundColor" white="1" alpha="1" colorSpace="custom" customColorSpace="calibratedWhite" /> |
|||
<constraints> |
|||
<constraint firstItem="kId-c2-rCX" firstAttribute="centerY" secondItem="iN0-l3-epB" secondAttribute="bottom" multiplier="1/3" constant="1" id="5cJ-9S-tgC" /> |
|||
<constraint firstAttribute="centerX" secondItem="kId-c2-rCX" secondAttribute="centerX" id="Koa-jz-hwk" /> |
|||
<constraint firstAttribute="bottom" secondItem="8ie-xW-0ye" secondAttribute="bottom" constant="20" id="Kzo-t9-V3l" /> |
|||
<constraint firstItem="8ie-xW-0ye" firstAttribute="leading" secondItem="iN0-l3-epB" secondAttribute="leading" constant="20" symbolic="YES" id="MfP-vx-nX0" /> |
|||
<constraint firstAttribute="centerX" secondItem="8ie-xW-0ye" secondAttribute="centerX" id="ZEH-qu-HZ9" /> |
|||
<constraint firstItem="kId-c2-rCX" firstAttribute="leading" secondItem="iN0-l3-epB" secondAttribute="leading" constant="20" symbolic="YES" id="fvb-Df-36g" /> |
|||
</constraints> |
|||
<nil key="simulatedStatusBarMetrics" /> |
|||
<freeformSimulatedSizeMetrics key="simulatedDestinationMetrics" /> |
|||
<point key="canvasLocation" x="548" y="455" /> |
|||
</view> |
|||
</objects> |
|||
</document> |
|||
@ -0,0 +1,183 @@ |
|||
#nullable enable |
|||
|
|||
using System; |
|||
using System.Diagnostics.CodeAnalysis; |
|||
using Avalonia.Controls.Platform; |
|||
using Avalonia.Platform; |
|||
using CoreGraphics; |
|||
using ObjCRuntime; |
|||
using UIKit; |
|||
|
|||
namespace Avalonia.iOS |
|||
{ |
|||
internal class NativeControlHostImpl : INativeControlHostImpl |
|||
{ |
|||
private readonly AvaloniaView _avaloniaView; |
|||
|
|||
public NativeControlHostImpl(AvaloniaView avaloniaView) |
|||
{ |
|||
_avaloniaView = avaloniaView; |
|||
} |
|||
|
|||
public INativeControlHostDestroyableControlHandle CreateDefaultChild(IPlatformHandle parent) |
|||
{ |
|||
return new UIViewControlHandle(new UIView()); |
|||
} |
|||
|
|||
public INativeControlHostControlTopLevelAttachment CreateNewAttachment(Func<IPlatformHandle, IPlatformHandle> create) |
|||
{ |
|||
var holder = new UIViewControlHandle(_avaloniaView); |
|||
NativeControlAttachment? attachment = null; |
|||
try |
|||
{ |
|||
var child = create(holder); |
|||
// It has to be assigned to the variable before property setter is called so we dispose it on exception
|
|||
#pragma warning disable IDE0017 // Simplify object initialization
|
|||
attachment = new NativeControlAttachment(holder, child); |
|||
#pragma warning restore IDE0017 // Simplify object initialization
|
|||
attachment.AttachedTo = this; |
|||
return attachment; |
|||
} |
|||
catch |
|||
{ |
|||
attachment?.Dispose(); |
|||
holder?.Destroy(); |
|||
throw; |
|||
} |
|||
} |
|||
|
|||
public INativeControlHostControlTopLevelAttachment CreateNewAttachment(IPlatformHandle handle) |
|||
{ |
|||
return new NativeControlAttachment(new UIViewControlHandle(_avaloniaView), handle) |
|||
{ |
|||
AttachedTo = this |
|||
}; |
|||
} |
|||
|
|||
public bool IsCompatibleWith(IPlatformHandle handle) => handle.HandleDescriptor == UIViewControlHandle.UIViewDescriptor; |
|||
|
|||
private class ViewHolder : UIView |
|||
{ |
|||
public ViewHolder(IntPtr handle) : base(new NativeHandle(handle)) |
|||
{ |
|||
|
|||
} |
|||
} |
|||
|
|||
private class NativeControlAttachment : INativeControlHostControlTopLevelAttachment |
|||
{ |
|||
// ReSharper disable once NotAccessedField.Local (keep GC reference)
|
|||
private IPlatformHandle? _child; |
|||
private UIViewControlHandle? _holder; |
|||
private UIView? _view; |
|||
private NativeControlHostImpl? _attachedTo; |
|||
|
|||
public NativeControlAttachment(UIViewControlHandle holder, IPlatformHandle child) |
|||
{ |
|||
_holder = holder; |
|||
_child = child; |
|||
|
|||
_view = (child as UIViewControlHandle)?.View ?? new ViewHolder(child.Handle); |
|||
_holder.View.AddSubview(_view); |
|||
} |
|||
|
|||
[MemberNotNull(nameof(_view))] |
|||
private void CheckDisposed() |
|||
{ |
|||
if (_view == null) |
|||
throw new ObjectDisposedException(nameof(NativeControlAttachment)); |
|||
} |
|||
|
|||
public void Dispose() |
|||
{ |
|||
_view?.RemoveFromSuperview(); |
|||
_holder = null; |
|||
_child = null; |
|||
_attachedTo = null; |
|||
_view?.Dispose(); |
|||
_view = null; |
|||
} |
|||
|
|||
public INativeControlHostImpl? AttachedTo |
|||
{ |
|||
get => _attachedTo; |
|||
set |
|||
{ |
|||
CheckDisposed(); |
|||
|
|||
_attachedTo = (NativeControlHostImpl?)value; |
|||
if (_attachedTo == null) |
|||
{ |
|||
_view.RemoveFromSuperview(); |
|||
} |
|||
else |
|||
{ |
|||
_holder!.View.AddSubview(_view); |
|||
} |
|||
} |
|||
} |
|||
|
|||
public bool IsCompatibleWith(INativeControlHostImpl host) => host is NativeControlHostImpl; |
|||
|
|||
public void HideWithSize(Size size) |
|||
{ |
|||
CheckDisposed(); |
|||
_view.Hidden = true; |
|||
} |
|||
|
|||
public void ShowInBounds(Rect bounds) |
|||
{ |
|||
CheckDisposed(); |
|||
if (_attachedTo == null) |
|||
throw new InvalidOperationException("The control isn't currently attached to a toplevel"); |
|||
|
|||
//bounds = _attachedTo._avaloniaView.ContentScaleFactor;
|
|||
_view.Frame = new CGRect(bounds.X, bounds.Y, bounds.Width, bounds.Height); |
|||
_view.Hidden = false; |
|||
} |
|||
} |
|||
} |
|||
|
|||
public class UIViewControlHandle : INativeControlHostDestroyableControlHandle, IDisposable |
|||
{ |
|||
internal const string UIViewDescriptor = "UIView"; |
|||
|
|||
private UIView? _view; |
|||
|
|||
public UIViewControlHandle(UIView view) |
|||
{ |
|||
_view = view; |
|||
} |
|||
|
|||
public UIView View => _view ?? throw new ObjectDisposedException(nameof(UIViewControlHandle)); |
|||
|
|||
public string HandleDescriptor => UIViewDescriptor; |
|||
|
|||
IntPtr IPlatformHandle.Handle => _view?.Handle.Handle ?? default; |
|||
|
|||
public void Destroy() |
|||
{ |
|||
Dispose(true); |
|||
} |
|||
|
|||
void IDisposable.Dispose() |
|||
{ |
|||
Dispose(true); |
|||
} |
|||
|
|||
~UIViewControlHandle() |
|||
{ |
|||
Dispose(false); |
|||
} |
|||
|
|||
private void Dispose(bool disposing) |
|||
{ |
|||
_view?.Dispose(); |
|||
_view = null; |
|||
if (disposing) |
|||
{ |
|||
GC.SuppressFinalize(this); |
|||
} |
|||
} |
|||
} |
|||
} |
|||